Filter a list with streams based on ids from another list - java

I have a list of Product objects with 30 objects.
I have created a fix list with strings of ids.
List<String> productsIdsForFreeSchoolYear = Arrays.asList("169", "172", "198", "213", "358", "4529", "6602", "5958");
What I want to do is to get list of Product only contains the ids from productsIdsForFreeSchoolYear list.
This is what i tried, but its seems its only check if the ids exists? Im i right?
productsSelectable.stream()
.distinct()
.filter(productsIdsForFreeSchoolYear::contains)
.collect(Collectors.toList());
productsSelectable is the list of Product.
Thank you!

In your code, contains() will always return false because it's a list of IDs (strings), and you're passing it a Product instance. You probably want something like this:
productsSelectable.stream()
.distinct()
.filter(p -> productsIdsForFreeSchoolYear.contains(p.getId()))
.collect(Collectors.toList());

Related

Java - order a collections by number of objects in each list?

Collection<List<Person>> personsByDepartment=
persons.stream()
.collect(Collectors.groupingBy(Person::getDepartment))
.values();
I have the following Collection above that groups a list of People into lists based off their department. This is working as expected.
How can I ensure this list is sorted so that the list with most amount of people in it is first?
Provide a Comparator to compare the lists by their size. You could stream the values again to sort:
Collection<List<Person>> personsByDepartment = persons.stream()
.collect(Collectors.groupingBy(Person::getDepartment))
.values()
.stream()
.sorted((l1, l2) -> Integer.compare(l2.size(), l1.size()))
.collect(Collectors.toList());
Edit: As Michael pointed out in comments the same comparator can be written as:
Comparator.<List<Person>>comparingInt(List::size).reversed()
It's more obvious, that the ordering is being reversed.

Remove items from list by comparing items from other List

Hello I am trying to remove items from a list by checking other list.It looks something like this ,
List<Model> list1 = Arrays.asList(new Model("pink",4),new Model("red",3))
List<Model> list2 = Arrays.asList(new Model("pink",4),new Model("pink",3),new Model("violet",9))
I want to remove both pink items from list2 as the same contains in list1,therefore I cannot use equals as I am comparing only using color.
How to do it in a good way ,maybe using streams ?
The final result should look like :
[Model("violet",9)]
Trivially, the answer to your question is: impossible - you can't remove anything from a List obtained by invoking Arrays.asList (you can .set, but you can't add, remove, or in any other way change the size; it's just a light wrapper around an array and arrays are fixed size in java). So let's assume you have 2 ArrayList instances, possibly created with new ArrayList<>(Arrays.asList(...)), then you can remove things.
The data structure you've chosen is inefficient. The 'best' way, if constrained by 'I have 2 lists', is to just... take it one step at a time:
Set<String> forbiddenColors = list1.stream()
.map(Model::getColor)
.collect(Collectors.toSet());
list2.removeIf(x -> forbiddenColors.contains(x.getColor()));
By using list, the speed of this should be ~O(n), vs. O(n^2). It's not going to matter unless you have many thousands of things in these lists, but just in case you do - this won't perform badly.
If list2 is mutable you can simply use removeIf
list2.removeIf(model->list1.stream().anyMatch(m->m.getColor().equals(model.getColor())));
If list2 is immutable then you can use filter and collect the output into another list
List<Model> result = list2.stream()
.filter(model->list1.stream()
.noneMatch(m->m.getColor().equals(m.getColor())))
.collect(Collectors.toList());

Update a single object from a list using stream

I have a list of objects. I need to update a single object from the list that match my filter. I can do something like below:
List<MyObject> list = list.stream().map(d -> {
if (d.id == 1) {
d.name = "Yahoo";
return d;
}
return d;
});
But my worry is i am like iterating through the whole list which may be up to 20k records. I can do a for loop then break, but that one I think also will be slow.
Is there any efficient way to do this?
Use findFirst so after finding the first matching element in the list remaining elements will not be processed
Optional<MyObject> result = list.stream()
.filter(obj->obj.getId()==1)
.peek(o->o.setName("Yahoo"))
.findFirst();
Or
//will not return anything but will update the first matching object name
list.stream()
.filter(obj->obj.getId()==1)
.findFirst()
.ifPresent(o->o.setName("Yahoo"));
You can use a Map instead of a list and save the id as a key.
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html
Then you can extract it with O(1).
It depends on how often you need to perform this logic on your input data.
If it happens to be several times, consider using a Map as suggested by Raz.
You can also transform your List into a Map using a Stream:
Map<Integer, MyObject> map = list.stream()
.collect(Collectors.toMap(
MyObject::getId
Function.identity()
));
The first argument of toMap maps a stream item to the corresponding key in the map (here the ID of MyObject), and the second argument maps an item to the map value (here the MyObject item itself).
Constructing the map will cost you some time and memory, but once you have it, searching an item by ID is extremely fast.
The more often you search an item, the more constructing the map first pays off.
However, if you only ever need to update a single item and then forget about the whole list, just search for the right element, update it and you're done.
If your data is already sorted by ID, you can use binary search to find your item faster.
Otherwise, you need to iterate through your list until you find your item. In terms of performance, nothing will beat a simple loop here. But using Stream and Optional as shown in Deadpool's answer is fine as well, and might result in clearer code, which is more important in most cases.
.stream().peek(t->t.setTag(t.getTag().replace("/","")));
Do anything you want with peek() meyhod

Return specific object from an ArrayList

I have a ArrayList with thousands of properties with many variables(including city). I need access/return only the properties from a specific city e.g all properties from Surrey. How do i get them?
I know how to search for them, by doing city.values("Surrey"). But I do not know how output the values.
I assume you want to search in your AirbnbListing list. You can use Java Stream. Use the filter method for that:
List<AirbnbListing> matchingListings = listings.stream()
.filter(l -> "Surrey".equals(l.getCity()))
.collect(Collectors.toList());
If you want a list of all cities, you can use the map method:
List<String> matchingListings = listings.stream()
.map(l -> l.getCity())
.collect(Collectors.toList());
Additionally here is an official Java stream explanation tutorial.
If you are using Java 8 or higher there is following option:
list.stream().filter(x -> "Berlin".equals(x.getCity())); //This filters the list and returns a list with city = Berlin.
Hopefully this is what you are looking for.

List containing another list to set using lambda in java

I have a list containing Persons, each person has a list with his subjects inside.
I need to return a Set containing every subject using lambda, so far i've tried this:
list.stream().map(person -> person.getSubjects());
But that would get me a List> so i can't use it.
How could i print/get every string in the list of every person using lambdas?
Thanks.
list.stream().map(person -> person.getSubjects().stream()); is not a List, it's a Stream. If you want a Set, do this :
list.stream().flatMap(person -> person.getSubjects().stream()).collect(Collectors.toSet());
This will create an HashSet<Subject>. Note the use of flatMap instead of map to flatten the lists of subjects into a single stream. If you want another implementation of Set, for example TreeSet, do the following :
list.stream().flatMap(person -> person.getSubjects().stream())
.collect(Collectors.toCollection(TreeSet::new));
You can use flatMap:
Set<Subject> subjects = list.stream()
.map(person -> person.getSubjects())
.flatMap(subjects -> subjects.stream())
.collect(Collectors.toSet());
flatMap is good for "flattening" nested collections.

Categories

Resources