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

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;

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

Sort by a list by name and age descending

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]

ICollection properties Sorting in ASP.Core using LINQ

How to sort Icollection properties.For example:
public class INSTRUCTOR
{
public int Id
public string Name
public PersonalDetails PersonalDetail
}
public class PersonalDetails
{
public int Id;
public string Firstname;
public ICollection<Emails> Emails;
}
public class Emails
{
public int Id;
public string Email;
}
Now I have the list pages of instructor and I got all instructors like this:
var instructors = db.instructors.include(p=>p.personaldetails).Tolist();
I can sort using instructorName using the following Code:
instructors = instructors.OrderBy(i => i.PersonalDetail.Firstname).ToList();
But, I want to know how to do with using emails which is ICollection?
In some way you're going to have to flatten the Email collection in order to sort the parent collection by it, either by applying some aggregate function or by for example joining it into a string.
If you for example make the assumption that the first Email address in the list is the primary address, then it could make sense to sort only by this item;
var orderedList =
instructors.OrderBy(i => i.PersonalDetails.Emails.FirstOrDefault()?.EmailAddress).ToList();
You could also choose to order by the "smallest" email address in the list by applying the Min() aggregate function;
var orderedList =
instructors.OrderBy(x => x.PersonalDetails.Emails.Min(e => e.EmailAddress)).ToList();

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.

Create a list of one object type from a list of another using Linq

If I have classes of Type A and B:
public class A
{
public int TotalCount;
public string Title;
}
public class B
{
public int Count;
public string Title;
}
I have a list of instances A instances, what is the most efficient way to create and populate a List of type B using Linq?
List<B> listB = listA.Select(a => new B()
{
Count = a.TotalCount,
Title = a.Title
}).ToList();
Does the same as eduncan's solution with different syntax. Take your pick..
var list =
from a in TableA
select new B {
Count = a.TotalCount,
Title = a.Title
};
You new up an instance of B in your select clause, and assign the properties using the inline property assignment feature in C# 3.0.
The advantages of mapping it inline comes from deferred execution of your Linq statement. Linq will map the modified statement, and execute it from your IQueryable. For example:
public class ClassA
{
public int TotalCount;
public string Title;
}
public class ClassB
{
public int Count;
public string Title;
}
public IQueryable<ClassB> FetchAllOfClassB()
{
var list =
from a in TableOfClassA
select new ClassB {
Count = a.TotalCount,
Title = a.Title
};
return list.AsQueryable();
}
Technically, the AsQueryable() is a bit redundant. Sometimes I use it to make it a point, others say it is absolutely required. None the less, the list object itself is IQueryable of ClassB.
Then you can call FetchAllOfClassB() further up the chain, and use IQuerable. It's pretty slick, and efficient.
Hmm, off the top of my head (so there will probably be errors!):
List< B > = from a in listOfA select new B(a.count, a.title);
might do the trick.

Resources