Home > front end >  Flatten List<A> where each A contains a List<B> for every distinct B
Flatten List<A> where each A contains a List<B> for every distinct B

Time:03-31

My goal is to expand List<A>, where each A has an attribute List<B> to a List<A>, where each A will contain a list with a single distinct object B.

Considering A and B have this structure:

class A{
  String idA;
  List<B> listOfB;
}

class B{
  Long idB;
  String name;
}

I'd like to expand the List<A> so that it contains an object A for each distinct contained object B (where two objects B are differentiated by the attribute name). For example

[
  {
    "idA": 1,
    "listOfB": [
      {
        "idB": 3,
        "name": "Foo"
      }
    ]
  },
  {
    "idA": 2,
    "listOfB": [
      {
        "idB": 3,
        "name": "Foo"
      },
      {
        "idB": 4,
        "name": "Bar"
      }
    ]
  }
]

should be transformed to

  {
    "idA": 1,
    "listOfB": [
      {
        "idB": 3,
        "name": "Foo"
      }
    ]
  },
  {
    "idA": 2,
    "listOfB": [
      {
        "idB": 3,
        "name": "Foo"
      }
    ]
  },
  {
    "idA": 2,
    "listOfB": [
      {
        "idB": 4,
        "name": "Bar"
      }
    ]
  }
]

How can I do this using streams?

CodePudding user response:

If you implement equals in B, this could be one of the solution.

        Set<B> bs = as.stream()
                .map(A::getListOfB)
                .flatMap(List::stream)
                .collect(Collectors.toSet());

        return bs.stream()
                .map(s -> as.stream()
                        .filter(a -> a.getListOfB().contains(s))
                        .map(a -> new A(a.idA, List.of(s)))
                        .collect(Collectors.toList())
                )
                .flatMap(List::stream)
                .collect(Collectors.toList());

Please note I have created all param constructor in both A & B.

CodePudding user response:

If I understood correctly your explanation and example you've provided, you need to build a resulting list comprised of objects A, where each element will hold a reference to a list with one element of type B.

A singleton-list and a new object A that will hold this list have to be created for every distinct object B initially contained in a particular object A.

For that, you can use a flatMap() operation, which takes an element and returns a stream of flattened elements. Operation distinct() applied in the nested stream will ensure that only unique objects B will be peeked from each list (I assume that hashCode/equals contract was correctly implemented in the class B).

List<A> listB = getSourceList();

listB.stream()
        .flatMap(a -> a.getListOfB().stream()
                  .distinct()
                  .map(b -> new A(a.getIdA(), List.of(b))))
        .collect(Collectors.toList());
  • Related