Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Guava "zip" two lists

Tags:

java

guava

Using Google Guava (Google Commons), is there a way to merge two equally sized lists into one list, with the new list containing composite objects of the two input lists?

Example:

public class Person {
    public final String name;
    public final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        return "(" + name + ", " + age + ")";
    }
}

and

List<String> names = Lists.newArrayList("Alice", "Bob", "Charles");
List<Integer> ages = Lists.newArrayList(42, 27, 31);

List<Person> persons =
    transform with a function that converts (String, Integer) to Person
System.out.println(persons);

Would output:

[(Alice, 42), (Bob, 27), (Charles, 31)]
like image 867
Steve Kuo Avatar asked Sep 27 '13 00:09

Steve Kuo


5 Answers

As of Guava 21, this is possible via Streams.zip():

List<Person> persons = Streams.zip(names.stream(), ages.stream(), Person::new)
                              .collect(Collectors.toList());
like image 185
shmosel Avatar answered Oct 21 '22 16:10

shmosel


Looks like this is not currently in Guava, but is a desired feature. See this github issue, in particular Iterators.zip().

like image 27
Steve Kuo Avatar answered Oct 21 '22 15:10

Steve Kuo


Just pretend this is a Guava method:

for (int i = 0; i < names.size(); i++) {
    persons.add(new Person(names.get(i), ages.get(i)));
}
like image 21
stepanian Avatar answered Oct 21 '22 17:10

stepanian


You can refer to underscore-java library.

Underscore-java is a port of Underscore.js for Java, and the zip method can achieve the goal.

Following is a sample code & output :

$.zip(Arrays.asList("moe", "larry", "curly"), Arrays.asList("30", "40", "50"));

=> [[moe, 30], [larry, 40], [curly, 50]]

like image 2
sheldon shen Avatar answered Oct 21 '22 15:10

sheldon shen


Here's a generic way to zip lists with vanilla Java. Lacking tuples, I opted to use a list of map entries (If you don't like to use map entries, introduce an additional class ZipEntry or something).

public static <T1,T2> List<Map.Entry<T1,T2>> zip(List<T1> zipLeft, List<T2> zipRight) {
    List<Map.Entry<T1,T2>> zipped = new ArrayList<>();
    for (int i = 0; i < zipLeft.size(); i++) {
        zipped.add(new AbstractMap.SimpleEntry<>(zipLeft.get(i), zipRight.get(i)));
    }
    return zipped;
}

To support arrays as well:

@SuppressWarnings("unchecked")
public static <T1,T2> Map.Entry<T1,T2>[] zip(T1[] zipLeft, T2[] zipRight) {
    return zip(asList(zipLeft), asList(zipRight)).toArray(new Map.Entry[] {});
}

To make it more robust add precondition checks on list sizes etc, or introduce left join / right join semantics similar to SQL queries.

like image 1
Benny Bottema Avatar answered Oct 21 '22 17:10

Benny Bottema