Sort by a list by name and age descending - sorting

I am trying to sort a list by lastName and age descending using Java 8 - I can get to the point of sorting both firstName and age - but not by firstName and age descending. Please help.
#Getter
#Setter
#AllArgsConstructor
#ToString
class ABCE {
private String firstName;
private String lastName;
private int age;
}
// Sort by firstName and then by age descending
list = list.stream().sorted(Comparator.comparing(ABCE::getFirstName).thenComparing(ABCE::getAge))
.collect(Collectors.toList());
list.stream().forEach(System.out::println);

Did you want both name and age descending? Your reversed() reverses all the comparisons prior. If you just want to reverse based on ages, do it as follows. I simplified it somewhat by using a record with just a first name.
record ABCE(String getFirstName, int getAge) {
}
public static void main(String[] args) {
List<ABCE> list = List.of(new ABCE("A", 10),
new ABCE("B", 10), new ABCE("B", 12),
new ABCE("C", 10), new ABCE("C", 10));
// Sort by firstName ascending and then by age descending
list = list.stream()
.sorted(Comparator.comparing(ABCE::getFirstName)
.thenComparing(ABCE::getAge,
Comparator.reverseOrder()))
.collect(Collectors.toList());
list.stream().forEach(System.out::println);
}
Prints
ABCE[getFirstName=A, getAge=10]
ABCE[getFirstName=B, getAge=12]
ABCE[getFirstName=B, getAge=10]
ABCE[getFirstName=C, getAge=10]
ABCE[getFirstName=C, getAge=10]

Related

How to convert List to Map - Key of Map should be a combination of multiple keys

How to convert List to Map - Key of Map should be a combination of multiple keys
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public class Student {
private long id;
private String firstName;
private String lastName;
private String street;
private String city;
public static void main(String[] args) {
List<Student> students = Arrays.asList(
Student.builder().id(1).firstName("John").lastName("Doe").build(),
Student.builder().id(1).firstName("Jane").lastName("Doe").build(),
Student.builder().id(1).firstName("Mike").lastName("Doe").build(),
Student.builder().id(1).firstName("Jack").lastName("Doe").build()
);
LinkedHashMap<Long, String> collect = students.stream()
.collect(Collectors.toMap(
Student::getId, Student::getFirstName, (x, y) -> x + ", " + y, LinkedHashMap::new));
System.out.println(collect);
// Answer I am expecting is Ex: {1johnDoe=[id=1,firstName=John, lastName=Doe]}
}
}
Using Java 17
I have implemented the below code using some of the latest java features apart from Java 8.
Records in java 14 : As of JDK 14, we can replace our data classes with records. Records are immutable classes that require only the type and name of fields. We do not need to create constructor, getters, setters, override toString() methods, override hashcode and equals methods.
List.of() in java 9: It is a static method that returns the immutable list of elements passed as arguments. Here in the below scenario, we will get the list of four student objects.
public class Test {
public static void main(String[] args) {
record Student(long id, String firstName, String lastName,String street, String city){}
Student s1 = new Student(1,"F1","L1","S1","C1");
Student s2 = new Student(2,"F2","L2","S2","C2");
Student s3 = new Student(3,"F3","L3","S3","C3");
Student s4 = new Student(4,"F4","L4","S4","C4");
Map<String,Student> output =
List.of(s1,s2,s3,s4).stream().collect(Collectors.toMap(x -> x.id() + x.firstName(),
Function.identity(), (k, v) -> k, LinkedHashMap::new));
System.out.println(output);
}
}
Output:
{1F1=Student[id=1, firstName=F1, lastName=L1, street=S1, city=C1], 2F2=Student[id=2, firstName=F2, lastName=L2, street=S2, city=C2], 3F3=Student[id=3, firstName=F3, lastName=L3, street=S3, city=C3], 4F4=Student[id=4, firstName=F4, lastName=L4, street=S4, city=C4]}
You can do as follows
LinkedHashMap<String, Student> collect = students.stream()
.collect(
Collectors.toMap(
student->(String.join("", Long.toString(student.getId()),student.getFirstName(),student.getLastName())),
student->student,
(v1,v2)->v1,
LinkedHashMap::new));
collect.forEach((k,v)->System.out.println(k+"="+v));
I think, you want key as String and value as Student into map

Use Java 8 stream to convert enum to set

I have an enum looks like:
public enum Movies {
SCIFI_MOVIE("SCIFI_MOVIE", 1, "Scifi movie type"),
COMEDY_MOVIE("COMEDY_MOVIE", 2, "Comedy movie type");
private String type;
private int id;
private String name;
Movies(String type, int id, String name) {
this.type = type;
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
}
I know that I can use stream to create a Set of Movies enum with:
Set<Movie> Movie_SET = Arrays.stream(Movie.values()).collect(Collectors.toSet());
What if I want to create a Set of enum Movies id. Is there a way to do that with stream?
Yes, assuming you have a getter for your id, your Movies enum might look like this:
public enum Movies {
SCIFI_MOVIE("SCIFI_MOVIE", 1, "Scifi movie type"),
COMEDY_MOVIE("COMEDY_MOVIE", 2, "Comedy movie type");
private String type;
private int id;
private String name;
Movies(String type, int id, String name) {
this.type = type;
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
}
Then, you can get the set of ids by using Stream.map():
Set<Integer> movieIds = Arrays.stream(Movies.values()).map(Movies::getId)
.collect(Collectors.toSet());
BTW, an alternative way to create the set of all movies is to use EnumSet.allOf():
Set<Integer> movieIds = EnumSet.allOf(Movies.class).stream().map(Movies::getId)
.collect(Collectors.toSet());
You can use EnumSet implementation for this purpose e.g.
For obtaining the Set of Movies:
Set<Movies> movies = EnumSet.allOf(Movies.class);
For obtaining only movies ids:
Set<Integer> moviesIds = movies.stream().map(Movies::getId).collect(Collectors.toSet());
If you are able to get stream of Enum values then rest could easily be achieved.
You could use custom Collector impl (My favorite of all time), see example below of a custom collector:-
Set<Integer> movieIds = Arrays
.stream(Movies.values())
.collect(
HashSet::new,
(set, e) -> set.add(e.getId()),
HashSet::addAll
);
Or you could use map to fetch ids only and the collect to collect these ids to a Set as usual.
Set<Integer> movieIds = Arrays
.stream(Movies.values())
.map(Movies::getId)
.collect(Collectors.toSet());

Return a string of all traders’ names sorted alphabetically with spaces in java8?

I was just playing with Java 8 streams and trying to return a string of all traders’ names sorted alphabetically.
public class PuttingIntoPractice {
public static void main(String[] args) {
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario","Milan");
Trader alan = new Trader("Alan","Cambridge");
Trader brian = new Trader("Brian","Cambridge");
List<Transaction> transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
// Query 4: Return a string of all traders’ names sorted alphabetically.
String traderStr = transactions.stream()
.map(t -> t.getTrader().getName())
.sorted()
.reduce("", (n1, n2) -> n1 + n2);
System.out.println(traderStr);
}
}
The output I get is like below:
AlanBrianMarioMarioRaoulRaoul
I want to show like below
Alan Brian Mario Mario Raoul Raoul
How can I do that ?
I used lombok to generate setter getter all those boiler plates
#Setter #Getter
#ToString
#AllArgsConstructor
public class Trader {
private String name;
private String city;
}
Transaction
#Setter #Getter
#ToString
#AllArgsConstructor
public class Transaction {
private Trader trader;
private int year;
private int value;
}
The problem in your code is how you are reducing the Stream.
As currently written, this reduce .reduce("", (n1, n2) -> n1 + n2); will simply concatenate the 2 String without any whitespace. To achieve what you want, you have to add to change the n1 + n2 to n1 + " " + n2. However, since you want to collect all the elements of the Stream into a single String, you should useCollectors.
Therefore, you should change your code to:
String traderStr = transactions.stream()
.map(t -> t.getTrader().getName())
.sorted()
.collect(Collectors.joining(" "));

reduce two group in java streaming api

i want to group the item element according to date such that , if item object's date is greater than present date then , it will fall into futuredate group and if date is less than present date then it will fall into pastdate
and then i want to reduce on these group so that i can get aggregate qty,price of these group, further aggregate value returned from pastdate group shold sit in oldPrice and oldQty of object
public class Item {
private String name;
private int qty;
private int oldQty;
private BigDecimal price;
private BigDecimal oldPrice;
Private Date date;
//constructors, getter/setters
}
Map<String, List<Item>> groupByP =
items.stream().collect(Collectors.groupingBy((row)->{
//logic to seperate items into two group based on date
}));
How to proceed after this
There are two ways to this, the first is first partition and later compute:
(this partitions, then return a Pair that holds the sum of all quantities and an average of the price)
Map<Boolean, List<Item>> partioned = items.stream()
.collect(Collectors.partitioningBy(item -> item.getDate().compareTo(now) > 0));
partioned.get(Boolean.FALSE).stream()
.map(item -> new AbstractMap.SimpleEntry<>(item.getOldQty(), item.getOldPrice()))
.reduce((entry1, entry2) -> {
int sum = entry1.getKey() + entry2.getKey();
BigDecimal averagePrice = entry1.getValue().add(entry2.getValue()).divide(BigDecimal.valueOf(2));
return new AbstractMap.SimpleEntry<>(sum, averagePrice);
}).get();
The second us writing a custom collector, but I doubt it will be more clear.
If you want to split items into to groups you need to use partitioningBy collector.
#AllArgsConstructor
#Getter
public static class Item {
private String name;
private int qty;
private int oldQty;
private BigDecimal price;
private BigDecimal oldPrice;
private Date date;
}
public static void main(String [] args) {
List<Item> items = Lists.newArrayList();
Map<Boolean, List<Item>> partitionedItems = items.stream()
.collect(Collectors.partitioningBy(item -> item.getDate().after(new Date())));
}
In this case you'll get
Map<Boolean, List<Item>>
Now you can:
map.get(true)
returns a list of items that match the condition
map.get(false)
returns a list of items that don't match the condition
Having those two lists you can make any reduction you want.

Using Linq to select from a List in a List with Contains

I'm having syntax troubles.
public class Student
{
int StudentId;
string Name;
}
public class Course
{
int CourseId;
List<Student> Students;
}
int[] studentIds = { 5, 7, 12 };
List<Course> allCourses = myDataContext.Courses.ToList();
Using Lambda expressions or query expressions, how do I get a filtered list of all the Courses containing any of the Students in the array studentIds?
var result = from course in allCourses
where course.Students.Any(x => studentIds.Contains(x.StudentId))
select course;

Resources