How to add values to list from Object using stream - java

I have a Object with multiple properties and I want to add multiple properties to same list
I was able to add one property couldn't find a way to add other property.
Can we do that if yes how ?
List<MyObject> myObject = new ArrayList<>();
I have some values in the object here
List<Long> Ids = myObject .stream().map(MyObject::getJobId).collect(Collectors.toList());
here I want to add one more property from same MyObject object getTestId to the list is there a way that I can add with in the same above statement ?

Create two lists using the streams then combine them in the end, map can only return one value either getJobId or getTestId, so you can't do it in the same iteration.
List<Long> JobIds = myObject.stream().map(myObj::getJobId).collect(Collectors.toList());
List<Long> TestIds = myObject.stream().map(myObj::getTestId).collect(Collectors.toList());
List<Long> Ids = Stream.concat(JobIds.stream(), TestIds.stream()).collect(Collectors.toList());

If you want a list containing the jobIds and the testIds then you can use something like this:
List<Long> ids = new ArrayList<>();
myObject.forEach(o -> {
ids.add(o.getJobId());
ids.add(o.getTestId());
});

In .map operation the object should be mapped to a list/stream of required ids and then apply flatMap:
List<Long> ids = myObject
.stream()
.map(mo -> Arrays.asList(mo.getId(), mo.getTestId()))
.flatMap(List::stream)
.collect(Collectors.toList());
or (same as Holger's comment)
List<Long> ids = myObject
.stream()
.flatMap(mo -> Stream.of(mo.getId(), mo.getTestId()))
.collect(Collectors.toList());
If the ids should be followed by testIds, the streams may be concatenated:
List<Long> ids = Stream.concat(
myObject.stream().map(MyObject::getId),
myObject.stream().map(MyObject::getTestId)
)
.collect(Collectors.toList());
If more than two properties should be listed one after another, Stream.of + flatMap should be used:
List<Long> ids = Stream.of(
myObject.stream().map(MyObject::getId),
myObject.stream().map(MyObject::getTestId),
myObject.stream().map(MyObject::getAnotherId)
)
.flatMap(Function.identity())
.collect(Collectors.toList());

Related

How to filter a List with two object of different class and with compare field in Java

I have two different list. I want to find and filter by field not on the other list. For example.
List<ObjectOne> List<ObjectTwo>
field | value field | value
{id=5, name="aaa"} {xId=4, text="aaa"}
{id=6, name="bbb"} {xId=6, text="bbb"}
{id=7, name="ccc"} {xId=5, text="ccc"}
If I want to filter one list, I am using org.springframework.cglib.core.CollectionUtils like that
CollectionUtils.filter(objectOne, s -> (
(ObjectOne) s).getId() == anyObject.getXId()
&& (ObjectOne) s).getName() == anyObject.getText());
But I want to compare two List, and I want to find noncontains value like that
objectOne = {id=5, name="aaa"} , {id=7, name="ccc"}
How am I filter with streamApi or any third-party libraries ?
noneMatch helps you here.
objectOnes.stream()
.filter(x -> objectTwos.stream()
.noneMatch(y -> y.text.equals(x.name) && y.xId == x.id))
.collect(Collectors.toList());
You can create a list of ObjectOne from the list of ObjectTwo as this:
List<ObjectOne> objectOne = listTwo.stream()
.map(x -> new ObjectOne(x.getxId(), x.getText()))
.collect(Collectors.toList());
And then you can use retainAll to find the common elements:
listOne.retainAll(objectOne);
if you wont modify the list of ObjectOne, then you can create a second list from listOne
List<ObjectOne> listOne2 = new ArrayList<>(listOne);
listOne2.retainAll(objectOne);
Note, this solution need to use hashcode and equals in ObjectOne.
I don't know how to do this with just one stream, but at least I got a solution for two.
List<ObjectOne> list1 = new ArrayList<>();
List<ObjectTwo> list2 = new ArrayList<>();
list1.stream()
.filter(o1 -> isInObjectTwoList(list2, o1))
.collect(Collectors.toList());
private boolean isInObjectTwoList(List<ObjectTwo> objectTwoList, ObjectOne o1) {
return objectTwoList.stream()
.filter(o2 -> o2.getText().equals(o1.getValue()))
.findAny()
.isPresent();
}

Java nested Map/List - filter by list contents, returning parent object

I have two string arguments to my function - "Pizza", and "Chips". I'd like to use streams to return the author whose "foods" key has contents matching these two strings
List<String> collection = Arrays.asList("Pizza", "Chips");
private static List<Map<String, Object>> authors = Arrays.asList(
ImmutableMap.of("id", "author-1",
"firstName", "Adam",
"lastName", "Awldridge",
"foods", Arrays.asList("Pizza", "Chips")),
ImmutableMap.of("id", "author-2",
"firstName", "Bert",
"lastName", "Bruce",
"foods", Arrays.asList("Pizza", "Fish")),
... // other authors
);
This is my attempt with streams:
return authors
.stream()
.filter(authors.stream()
.flatMap(author -> author.get("foods"))
.findAny(queryFoods))
.findFirst().orElse(null);
I want to return the first author who's foods match that of my query. I think the main difficulty is organizing data - unfortunately I can't get the following casting to work.
.flatMap(author -> (List<String>) author.get("foods"))
Also, this might stream through the authors too many times (I should use .filter on the stream I just made with
authors.stream()
Here you cannot directly treat the value of foods key as a List. Its just an Object. So, first you need to do an instance of check and if it is an instance of List, then you can check whether it contains the values that are there in your collection.
Map<String,Object> firstAuthor = authors
.stream()
.filter(author -> {
Object foods = author.get("foods");
if(foods instanceof List) {
List foodsList = (List) foods;
return foodsList.containsAll(collection);
}
return false;
})
.findFirst().orElse(null);
OUTPUT:
{id=author-1, firstName=Adam, lastName=Awldridge, foods=[Pizza, Chips]}
The above code will give you the required author if it exists or else null.
[Here, I have assumed that you want to check whether the author has all the food items that are present in collection object created by you. If you want to check for only one of the items then you can use contains() method from java.util.List instead of containsAll() method. Also, you will have to iterate over the collection object to check for each item in collection.]
I would solve it by filtering in stream:
Map<String,Object> author = authors.stream()
.filter(a -> a.containsKey("foods"))
.filter(a -> a.get("foods") instanceof List)
.filter(a -> ((List) a.get("foods")).containsAll(collection))
.findFirst().orElse(null);
Maybe this is what you want?
authors
.stream()
.filter(a -> a.get("foods").stream().anyMatch(x -> "Pizza".equals(x)))
.findFirst().orElse(null);

filtering of values in list based on values in set

I'm trying to use java 8 to solve the following issue. Say I have the following (A and B are custom classes)
ArrayList<A> skills;
HashSet<B> workCenters;
What I need to do is to find whether value a.getDepartment() which is a String also contained in B which also has a method String getDepartment() and then to collect those into new List<A>.
I tried such:
List<A> collect = skills.stream()
.filter(s -> workCenters.contains(s.getDepartment())
.collect(Collectors.toList());
but in this case i don't do it right because I couldn't retrieve getDepartment() from workCenters. What would be the correct solution?
You could start with converting HashSet<B> to HashSet<String> and then use your code:
Set<String> bDeps = workCenters.stream()
.map(B::getDepartment)
.collect(Collectors.toSet());
List<A> collect = skills.stream()
.filter(s -> bDeps.contains(s.getDepartment()))
.collect(Collectors.toList());
collect all the department in workCenters into a Set<String>let's say departmentSet.
List<A> collect = skills.stream()
.filter(s -> departmentSet.contains(s.getDepartment())
.collect(Collectors.toList());
First stream over the workCenters and get the set of departments in there, then stream over the skills and filter out any ones not present in that set.
final Set<String> workCenterDepartments = workCenters.stream()
.map(B::getDepartment)
.collect(Collectors.toSet());
final List<A> skillsWithWorkCenterDept = skills.stream()
.filter(skill -> workCenterDepartments.contains(skill.getDepartment()))
.collect(Collectors.toList());
If you don't need the old list any more, you might decide to remove elements from the previous list rather than create a new one:
skills.removeIf(skill -> !workCenterDepartments.contains(skill.getDepartment()));
List<B> workCenters = Collections.emptyList();
List<A> skills = Collections.emptyList();
Set<String> workCenterDepartments = workCenters.stream().map(B::getDepartment).collect(Collectors.toSet());
List<A> skillsWithWorkCenterDept = new ArrayList<>(skills);
skillsWithWorkCenterDept.removeIf(skill -> !workCenterDepartments.contains(skill.getDepartment()));

How do i group by the contents of a string list using collect() of a Stream?

I have a database object that has a field that contains a list of strings. I retrieve all these objects and then use the flatMap and distinct stream methods on the resulting list to get a new list that holds all possible unique values that a database object string list can contain.
Next i want to make a map where the keys are the unique values list of the stringlist that i made earlier, and the values of the map are a list of database objects whose stringlist contains the value of the respective string mapkey.
So what I want is groupingBy the following:
if(object.stringList().contains(respectiveMapKeyFromUniqeStringCollection) put object in object values list of that respective keymap.
Is something like this possible using the groupingBy method?
Edit: I will explain further
class VegetableMaker{
#ElementCollection
private List<String> vegetableList;
}
Lets assume the possible values that a vegetableList can contain are: "Lettuce, Tomato, spinache, rubarbe, onion"
Set<String> produceNames = vegetableMakers.stream().flatMap(vegetableMaker -> vegetableMaker.getVegetableList().stream())
.distinct().collect(Collectors.toSet());
Now we have the list that contains all the possible values mentioned before.
We want to use the values in this list as the keys in the map.
So the Map will look like:
Map<uniqueStringsAsKeys, List<VegetableMaker>> map
The list value contains all the VegetableMaker instances of which the vegetableList contains the key of the map. So the list of key Onion will contain all the VegetableMaker instances whose list includes "Onion".
Is it possible to achieve such a map using the groupingBy method of a java stream?
EDIT 2:
This is the solution i have now, that doesn't use groupingBy but clarifies even more what I want.
EDIT: changed variable in code to match variables used in previous examples.
Set<VegetableMaker> vegetableMakers = vegetableMakerDao.findAll();
Set<String> uniqueVegetableList = vegetableMakers.stream().flatMap(vegetableMaker -> affiliateLink.getKeywords().stream()).distinct().collect(Collectors.toSet());
Map<String,Set<VegetableMaker>> vegetableMakersContainingKeywordInTheirList = new HashMap<>();
uniqueVegetableList.forEach(produceName ->{
Set<VegetableMaker> vegetableMakerSet = new HashSet<>();
vegetableMakers.forEach( vegetableMaker -> {
if(vegetableMaker.getVegetableList().contains(produceName))
vegetableMakerSet.add(vegetableMaker);
});
vegetableMakersContainingKeywordInTheirList.put(produceName, vegetableMakerSet);
});
If I understood you correctly:
List<VegetableMaker> dbObjects = List.of(
new VegetableMaker("Salad", List.of("Onion", "Cucumber")),
new VegetableMaker("Italian Salad", List.of("Cheese")),
new VegetableMaker("Greek Salad", List.of("Onion")));
Map<String, List<VegetableMaker>> map = dbObjects.stream()
.flatMap(x -> x.getVegetableList().stream().map(y -> new SimpleEntry<>(x, y)))
.collect(Collectors.groupingBy(
Entry::getValue,
Collectors.mapping(Entry::getKey, Collectors.toList())));
System.out.println(map);
Resulting being something like:
{Onion=[Salad, Greek Salad], Cheese=[Italian Salad], Cucumber=[Salad]}
EDIT
This is not much different than what I posted above:
Map<String, Set<VegetableMaker>> result = vegetableMakerList.stream()
.flatMap(x -> x.getKeywords().stream().distinct().map(y -> new SimpleEntry<>(x, y)))
.collect(Collectors.groupingBy(
Entry::getValue,
Collectors.mapping(Entry::getKey, Collectors.toSet())));
final Set<VegetableMaker> vegetableMakers = vegetableMakerDao.findAll();
final Map<String, Set<VegetableMaker>> vegetableMakersContainingKeywordInTheirList = vegetableMakers.stream()
.map(VegetableMaker::getKeywords)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toMap(
Function.identity(),
vegetable -> vegetableMakers.stream()
.filter(vegetableMaker -> vegetableMaker.getKeywords().contains(vegetable))
.collect(Collectors.toSet())
));

Map to List after filtering on Map's key using Java8 stream

I have a Map<String, List<String>>. I want to transform this map to a List after filtering on the map's key.
Example:
Map<String, List<String>> words = new HashMap<>();
List<String> aList = new ArrayList<>();
aList.add("Apple");
aList.add("Abacus");
List<String> bList = new ArrayList<>();
bList.add("Bus");
bList.add("Blue");
words.put("A", aList);
words.put("B", bList);
Given a key, say, "B"
Expected Output: ["Bus", "Blue"]
This is what I am trying:
List<String> wordsForGivenAlphabet = words.entrySet().stream()
.filter(x-> x.getKey().equalsIgnoreCase(inputAlphabet))
.map(x->x.getValue())
.collect(Collectors.toList());
I am getting an error. Can someone provide me with a way to do it in Java8?
Your sniplet wil produce a List<List<String>> not List<String>.
You are missing flatMap , that will convert stream of lists into a single stream, so basically flattens your stream:
List<String> wordsForGivenAlphabet = words.entrySet().stream()
.filter(x-> x.getKey().equalsIgnoreCase(inputAlphabet))
.map(Map.Entry::getValue)
.flatMap(List::stream)
.collect(Collectors.toList());
You can also add distinct(), if you don't want values to repeat.
Federico is right in his comment, if all you want is to get the values of a certain key (inside a List) why don't you simply do a get (assuming all your keys are uppercase letters already) ?
List<String> values = words.get(inputAlphabet.toUpperCase());
If on the other hand this is just to understand how stream operations work, there is one more way to do it (via java-9 Collectors.flatMapping)
List<String> words2 = words.entrySet().stream()
.collect(Collectors.filtering(x -> x.getKey().equalsIgnoreCase(inputAlphabet),
Collectors.flatMapping(x -> x.getValue().stream(),
Collectors.toList())));
As was previously told after collect you will get List<List<String>> with only one or zero value in it. You can use findFirst instead of collect it will return you Optional<List<String>>.

Categories