James
James

Reputation: 809

How do I get a list of items from a subset of a list, based on the items in another list in Java?

I've got a List<String> which represents the ID's (can be duplicate), of items from another list, which is a List<Cheat>, where each Cheat has a String ID and a List<Integer> RNG. Both have accessor methods in Cheat.

I need to convert this list of ID's, into a list of RNG's for each Cheat that I have been supplied with the ID for.

For example, I could have 3 Cheats:

1:{ID:1, RNG:{1,2,3}}
2:{ID:2, RNG{1,2}}
3:{ID:3, RNG:{1}}

And a List of ID's of:

{3,1,1,2}.

I would need to end up with a final list of {1,1,2,3,1,2,3,1,2}, which is the RNG's of Cheat 3, then the RNG's of cheat 1, then the RNG's of cheat 1 again, then finally the RNG's of cheat 2.

If anyone could help me out it would be appreciated. Thank you.

I've tried and failed with:

ImmutableList<Integer> sequenceRngs = cheatIds.stream()
                                              .map(s -> cheats.stream()
                                                                .filter(cheat -> cheat.getId().equals(s))
                                                                .findFirst()
                                                                .map(cheat -> cheat.getRng()))
                                              .flatMap(cheat -> cheat.getRng())
                                              .collect(ListUtils.toImmutableList());

Upvotes: 0

Views: 144

Answers (3)

Naman
Naman

Reputation: 31878

You can attain that with the following steps:

  1. Create a map of cheatId to RNG ids associated:

    Map<Integer, List<Integer>> map = cheats.stream()
            .collect(Collectors.toMap(Cheat::getId,
                    cheat -> cheat.getRng().stream().map(RNG::getId).collect(Collectors.toList())));
    
  2. Iterate over the cheatIds provided as input and get the corresponding RNG ids from the map to collect as output:

    List<Integer> output = cheatIds.stream()
            .flatMap(ch -> map.get(ch).stream())
            .collect(Collectors.toList());
    

Upvotes: 0

Marcus Held
Marcus Held

Reputation: 645

One possible solution:

import java.util.List;
import java.util.stream.Collectors;

class Scratch {

    static class Cheat {
        int id;
        List<Integer> rng;

        public Cheat(int id, List<Integer> rng) {
            this.id = id;
            this.rng = rng;
        }
    }

    public static void main(String[] args) {

        List<Cheat> allCheats = List.of(
                new Cheat(1, List.of(1,2,3)),
                new Cheat(2, List.of(1,2)),
                new Cheat(3, List.of(1))
        );

        List<Integer> result = List.of(3, 1, 1, 2).stream()
                .flatMap(id -> allCheats.stream()
                        .filter(cheat -> cheat.id == id)
                        .findFirst().orElseThrow().rng.stream())
                .collect(Collectors.toList());

        System.out.println(result);

    }
}

The key is to use flatMap to get the result in a single - not nested - Collection in the end.

Upvotes: 2

Forketyfork
Forketyfork

Reputation: 7810

The lambda that you pass to flatMap should return a Stream, not a List. And you should handle the case where there's no such element in the stream - even if you are sure there is. Something like this should do:

final ImmutableList<String> sequenceRngs = cheatIds.stream().flatMap(id ->
    cheats.stream().filter(cheat -> id.equals(cheat.getId()))
        .findAny().orElseThrow(IllegalStateException::new)
        .getRng().stream())
    .collect(ListUtils.toImmutableList());

Also, I would propose to convert the list of cheats to a map - that would simplify the code and reduce the complexity of searching from O(n) to O(1).

Upvotes: 1

Related Questions