-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
222 additions
and
0 deletions.
There are no files selected for viewing
9 changes: 9 additions & 0 deletions
9
08-lambdas-and-stream-api/lab/solution/src/bg/sofia/uni/fmi/mjt/football/Foot.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package bg.sofia.uni.fmi.mjt.football; | ||
|
||
public enum Foot { | ||
LEFT, RIGHT; | ||
|
||
public static Foot of(String foot) { | ||
return Foot.valueOf(foot.toUpperCase()); | ||
} | ||
} |
139 changes: 139 additions & 0 deletions
139
...and-stream-api/lab/solution/src/bg/sofia/uni/fmi/mjt/football/FootballPlayerAnalyzer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package bg.sofia.uni.fmi.mjt.football; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.Reader; | ||
import java.util.AbstractMap; | ||
import java.util.AbstractMap.SimpleEntry; | ||
import java.util.Comparator; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.NoSuchElementException; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
public class FootballPlayerAnalyzer { | ||
|
||
private static final int RATING_VARIANCE = 3; | ||
private final List<Player> players; | ||
|
||
public FootballPlayerAnalyzer(Reader reader) { | ||
try (var br = new BufferedReader(reader)) { | ||
players = br.lines()// | ||
.skip(1)// | ||
.map(Player::of)// | ||
.toList(); | ||
} catch (IOException ex) { | ||
throw new IllegalArgumentException("Could not load dataset", ex); | ||
} | ||
} | ||
|
||
public List<Player> getAllPlayers() { | ||
return List.copyOf(players); | ||
} | ||
|
||
public Set<String> getAllNationalities() { | ||
return players.stream()// | ||
.map(Player::nationality)// | ||
.collect(Collectors.toUnmodifiableSet()); | ||
} | ||
|
||
public Player getHighestPaidPlayerByNationality(String nationality) { | ||
if (null == nationality) { | ||
throw new IllegalArgumentException("Provided nationality cannot be null"); | ||
} | ||
|
||
return players.stream()// | ||
.filter(p -> p.nationality().equals(nationality))// | ||
.max(Comparator.comparingLong(Player::wageEuro))// | ||
.orElseThrow(NoSuchElementException::new); | ||
} | ||
|
||
public Map<Position, Set<Player>> groupByPosition() { | ||
return players.stream()// | ||
.flatMap(this::mapPlayerToPositionEntry)// | ||
.collect(Collectors.toUnmodifiableMap(SimpleEntry::getKey, this::newSetFromPlayerEntry, | ||
this::mergeSets)); | ||
} | ||
|
||
private Stream<SimpleEntry<Position, Player>> mapPlayerToPositionEntry(Player player) { | ||
return player.positions()// | ||
.stream()// | ||
.map(pos -> new AbstractMap.SimpleEntry<>(pos, player)); | ||
} | ||
|
||
private Set<Player> newSetFromPlayerEntry(SimpleEntry<Position, Player> playerEntry) { | ||
Set<Player> playerSet = new HashSet<>(); | ||
playerSet.add(playerEntry.getValue()); | ||
|
||
return playerSet; | ||
} | ||
|
||
private Set<Player> mergeSets(Set<Player> set1, Set<Player> set2) { | ||
set1.addAll(set2); | ||
|
||
return set1; | ||
} | ||
|
||
public Optional<Player> getTopProspectPlayerForPositionInBudget(Position position, long budget) { | ||
if (null == position) { | ||
throw new IllegalArgumentException("Provided position cannot be null"); | ||
} | ||
if (budget < 0) { | ||
throw new IllegalArgumentException("Provided budget cannot be negative"); | ||
} | ||
|
||
return players.stream()// | ||
.filter(p -> p.positions().contains(position))// | ||
.filter(p -> p.valueEuro() <= budget)// | ||
.max(Comparator.comparingDouble(this::calculatePlayerProspectRating)); | ||
} | ||
|
||
private double calculatePlayerProspectRating(Player player) { | ||
return ((double) player.overallRating() + player.potential()) / player.age(); | ||
} | ||
|
||
public Set<Player> getSimilarPlayers(Player player) { | ||
if (null == player) { | ||
throw new IllegalArgumentException("Provided player cannot be null"); | ||
} | ||
|
||
return players.stream()// | ||
.filter(p -> canPlayWithSameFoot(p, player))// | ||
.filter(p -> canPlaySamePosition(p, player))// | ||
.filter(p -> isSimilarRating(p, player))// | ||
.collect(Collectors.toUnmodifiableSet()); | ||
} | ||
|
||
private boolean canPlayWithSameFoot(Player player, Player targetPlayer) { | ||
return player.preferredFoot() == targetPlayer.preferredFoot(); | ||
} | ||
|
||
private boolean canPlaySamePosition(Player player, Player targetPlayer) { | ||
return player.positions()// | ||
.stream()// | ||
.anyMatch(pos -> targetPlayer.positions().contains(pos)); | ||
} | ||
|
||
private boolean isSimilarRating(Player player, Player targetPlayer) { | ||
int overallRating = player.overallRating(); | ||
int targetOverallRating = targetPlayer.overallRating(); | ||
|
||
return (targetOverallRating - RATING_VARIANCE) <= overallRating | ||
&& overallRating <= (targetOverallRating + RATING_VARIANCE); | ||
} | ||
|
||
public Set<Player> getPlayersByFullNameKeyword(String keyword) { | ||
if (null == keyword) { | ||
throw new IllegalArgumentException("Provided keyword cannot be null"); | ||
} | ||
|
||
return players.stream()// | ||
.filter(p -> p.fullName().contains(keyword))// | ||
.collect(Collectors.toUnmodifiableSet()); | ||
} | ||
|
||
} |
53 changes: 53 additions & 0 deletions
53
08-lambdas-and-stream-api/lab/solution/src/bg/sofia/uni/fmi/mjt/football/Player.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package bg.sofia.uni.fmi.mjt.football; | ||
|
||
import java.time.LocalDate; | ||
import java.time.format.DateTimeFormatter; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
public record Player(String name, String fullName, LocalDate birthDate, int age, double heightCm, double weightKg, | ||
List<Position> positions, String nationality, int overallRating, int potential, long valueEuro, long wageEuro, | ||
Foot preferredFoot) { | ||
|
||
private static final int NAME = 0; | ||
private static final int FULL_NAME = 1; | ||
private static final int BIRTH_DATE = 2; | ||
private static final int AGE = 3; | ||
private static final int HEIGHT_CM = 4; | ||
private static final int WEIGHT_KG = 5; | ||
private static final int POSITIONS = 6; | ||
private static final int NATIONALITY = 7; | ||
private static final int OVERALL_RATING = 8; | ||
private static final int POTENTIAL = 9; | ||
private static final int VALUE_EURO = 10; | ||
private static final int WAGE_EURO = 11; | ||
private static final int PREFERRED_FOOT = 12; | ||
|
||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("M/d/yyyy"); | ||
|
||
public static Player of(String line) { | ||
String[] tokens = line.split(";"); | ||
|
||
String name = tokens[NAME]; | ||
String fullName = tokens[FULL_NAME]; | ||
LocalDate birthDate = parseDate(tokens[BIRTH_DATE]); | ||
int age = Integer.parseInt(tokens[AGE]); | ||
double heightCm = Double.parseDouble(tokens[HEIGHT_CM]); | ||
double weightKg = Double.parseDouble(tokens[WEIGHT_KG]); | ||
List<Position> positions = Arrays.stream(tokens[POSITIONS].split(",")).map(Position::valueOf).toList(); | ||
String nationality = tokens[NATIONALITY]; | ||
int overallRating = Integer.parseInt(tokens[OVERALL_RATING]); | ||
int potential = Integer.parseInt(tokens[POTENTIAL]); | ||
long valueEuro = Long.parseLong(tokens[VALUE_EURO]); | ||
long wageEuro = Long.parseLong(tokens[WAGE_EURO]); | ||
Foot preferredFoot = Foot.of(tokens[PREFERRED_FOOT]); | ||
|
||
return new Player(name, fullName, birthDate, age, heightCm, weightKg, positions, nationality, overallRating, | ||
potential, valueEuro, wageEuro, preferredFoot); | ||
} | ||
|
||
private static LocalDate parseDate(String date) { | ||
return LocalDate.parse(date, DATE_FORMATTER); | ||
} | ||
|
||
} |
21 changes: 21 additions & 0 deletions
21
08-lambdas-and-stream-api/lab/solution/src/bg/sofia/uni/fmi/mjt/football/Position.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package bg.sofia.uni.fmi.mjt.football; | ||
|
||
public enum Position { | ||
// Diagram: https://fifauteam.com/fifa-21-positions/ | ||
|
||
ST, // Striker | ||
LM, // Left Midfielder | ||
CF, // Centre Forward | ||
GK, // Goalkeeper | ||
RW, // Right Winger | ||
CM, // Centre Midfielder | ||
LW, // Left Winger | ||
CDM, // Defensive Midfielder | ||
CAM, // Attacking midfielder | ||
RB, // Right back | ||
LB, // Left back | ||
LWB, // Left Wing-back | ||
RM, // Right Midfielder | ||
RWB, // Right Wing-back | ||
CB; // Centre Back | ||
} |