daffycricket
daffycricket

Reputation: 405

Merge several Collections<A> into a single Collection<A> from a Collection<B> using Guava

What I want to do is very simple using good old loops.

Say I have an object A that contains a List of Bs.

public class A
{
  public List<B> myListOfB;
}

In some other method, I have a List of As. Basically, what I want to do is merge all the elements in the lists of Bs of all my As.

For instance, it's very easy to write something like this using loops :

public List<B> someMethod(List<A> myListOfAs)
{
  List<B> toReturn = new ArrayList<A>();
  for(A myA : myListOfAs)
  {
    toReturn.addAll(myA.myListOfB);
  }
  return toReturn;
}

But I would like to do it in a more fonctionnal way using Guava. This example is very easy, but it could be much more complex with conditions for instance, hence it makes sense to use functional programming.

I'm very new to Guava. I've started using it for filtering and ordering, but I'm pretty sure it might also be possible to use it, but I haven't been able to figure out how. I found this Combine multiple Collections into a single logical Collection? but it doesn't really answer my question.

Upvotes: 4

Views: 4840

Answers (3)

Thomas Jung
Thomas Jung

Reputation: 33092

You can use a function to extract the list and concat to flatten the lists. This results in an Iterable.

List<A> input;
Function<A, List<B>> t = new Function<A, List<B>>() {
    @Override public List<B> apply(A input) { 
        return input.myListOfB; 
    }
};
Iterable<B> transform = Iterables.concat(Iterables.transform(input, t));

You can create an List if you need it:

ImmutableList<B> asList = ImmutableList.copyOf(transform);
//or
List<B> newArrayList = Lists.newArrayList(transform);

Note: Normally, public fields of classes are static and immutable or private. Everything else will bring you in trouble.

Upvotes: 6

Ray Greenwell
Ray Greenwell

Reputation: 41

If you made class A implement Iterable, you could use Iterables.concat().

public class A
    implements Iterable<B>
{
    public List<B> myListOfB;

    // from Iterable
    public Iterator<B> iterator ()
    {
        return myListOfB.iterator();
    }
}

Then:

List<A> listOfAs = ...;
Iterable<B> allBs = Iterables.concat(listOfAs);

To put that in another List:

List<B> listOfAllBs = Lists.newArrayList(allBs);

Upvotes: 4

epoch
epoch

Reputation: 16605

How about this? I just roughed up what i think you meant:

public static void main(String[] args) {

    A a1 = new A();
    a1.bs = Lists.newArrayList(new B("1"), new B("2"), new B("3"));
    A a2 = new A();
    a2.bs = Lists.newArrayList(new B("a"), new B("b"), new B("c"));
    A a3 = new A();
    a3.bs = Lists.newArrayList(new B("!"), new B("@"), new B("#"));

    List<A> as = Lists.newArrayList(a1, a2, a3);
    List<B> lettersAndNumbers = method(as, new Predicate<A>() {
        @Override
        public boolean apply(A input) {
            for (B s : input.bs) {
                if (s.bVal.matches("^[a-zA-Z0-9]*$")) {
                    return true;
                }
            }

            return false;
        }
    });

    System.out.println("as : " + lettersAndNumbers);
}


static List<B> method(List<A> as, Predicate<A> pred) {
    List<B> newbs = Lists.newArrayList();
    Iterable<A> filtered = Iterables.filter(as, pred);
    for (A a : filtered) {
        newbs.addAll(a.bs);
    }

    return newbs;
}

class A {
    List<B> bs;

    @Override
    public String toString() {
        return "A{" + bs.toString() + "}";
    }
}

class B {
    String bVal;
    B(String bVal) {
        this.bVal = bVal;
    }
    @Override
    public String toString() {
        return "B{" + bVal + "}";
    }
}

Upvotes: 1

Related Questions