4

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.

Lii
  • 11,553
  • 8
  • 64
  • 88
daffycricket
  • 405
  • 5
  • 14
  • What you want is a kind of `flatten` method. Cf. http://stackoverflow.com/questions/5949091/flattening-an-iterableiterablet-in-guava . – Frank Pavageau Apr 24 '12 at 10:20

3 Answers3

6

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.

Thomas Jung
  • 32,428
  • 9
  • 84
  • 114
  • Yes, I was thinking about something like 'transform'. That's what I meant without being able to figure it. Many thanks ! As fot the static fields, you're absolutely right: you should always encapsulate your fields properly. I put it like this to make the example as easy as possible. – daffycricket Apr 24 '12 at 22:03
4

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);
1

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 + "}";
    }
}
epoch
  • 16,396
  • 4
  • 43
  • 71
  • However, what I meant was I that I wanted to avoid the use of a loop on the main list. And in your example method() you create such a loop that goes through all the As, so it's not really what I'm looking for. But take a look at Thomas Jung's answer, I think it fits my needs. But thanks anyway for your time :) – daffycricket Apr 24 '12 at 22:17