1

I have two lists with the same number of arguments, and I'd like an elegant way of combining them (not concatenate).

Here is my current (not so good) way of doing it (just so you know what I am trying to do).

    List<Double> list1 = ... // init here
    List<Double> list2 = ... // init here
    Function<Double, Double, Double> myFunc = ... // init here

    List<Double> ret = new ArrayList<Double>(size);

    for (int n = 0; n < size; ++n)
    {
        ret.add(func.apply(list1.get(n),
                           list2.get(n)));
    }
    return ret;

interface Function <X, Y, Z>
{
    Z apply(X arg1, Y arg2);
}

Is there some existing helpers that would allow me to do something like:

Lists.combine(list1, list2, myFunction);

For instance, suppose I have two list of integers, and I have the function f(x, y) = x * y)

I want the resulting list to be a list of (x[i] * y[i])

Concretely,

list1 = {1, 2, 3, 4}
list2 = {2, 3, 4, 4}

result = {2, 6, 12, 15}

Thanks

One Two Three
  • 22,327
  • 24
  • 73
  • 114
  • No! That simply zip them together, not transforming thenm – One Two Three Oct 03 '13 at 21:56
  • Say I have two lists of integers, I want the resulting list to be the sum of the two elements from each list – One Two Three Oct 03 '13 at 21:57
  • 2
    Creating the `Function` anonymous inner class requires more awkward cruft than just writing the normal `for` loop you'd have without any fancy functional techniques. Really, it's a three-line `for` loop that's not especially complicated. – Louis Wasserman Oct 03 '13 at 22:00
  • What if the thing that you do with the two lists is VERY complicated or if you have to do this combing business a lot in your programs, but each time, the combination is done differently? You'd want a way to consistently combine the two lists, without having to know the specific details on what to do with each element, no? – One Two Three Oct 03 '13 at 22:03
  • 1
    @OneTwoThree: Doesn't matter. If it's that complicated, you pull that complicated logic out into a separate function -- which is equivalent to what you'd be doing anyway, with your `Function` approach. You're still repeating less code with the simple `for` loop. – Louis Wasserman Oct 03 '13 at 22:29
  • @OneTwoThree check my answer, and the Var-Args method ... you can substitute the with a generic type of your own declared in the method level, like "private T[] apply(int index, List...lists){...}, something like that – Ahmed Adel Ismail Oct 03 '13 at 22:36
  • 2, 6 12, 16? Instead of 15? – Uncle Iroh Oct 03 '13 at 22:42

2 Answers2

0

The main idea is to make a method, that can retrieve all your indexes of all your lists at once, and return this result in an array or a collection ... you wont have to store them, all you need is just a reference to them, and you can pass them as parameters to your method

here is some forms of creating such method, and you may make a common value to be returned in case your search returned invalid result :

private List<Double> firstList = new ArrayList<Double>();
private List<Double> secondList = new ArrayList<Double>();
private List<Double> thirdList = new ArrayList<Double>();
private static final double OUT_OF_BOUNDS = -1;

// just to initialize the arrays
private void initializeLists() {
    // dont pay attention to this
    for (double d1 = 0, d2 = 500, d3 = 5000; d1 < 50; d1++, d2++, d3++) {
        firstList.add(d1);
        secondList.add(d2);
        thirdList.add(d3);
    }
}

// create a method to retrieve data from both lists, and return it in an array or even
// a new list
private double[] apply(int index) {
    if (index < firstList.size() && index < secondList.size()) {
        if (index >= 0) {
            return new double[] { firstList.get(index), secondList.get(index) };
        }
    }
    return new double[] { OUT_OF_BOUNDS, OUT_OF_BOUNDS };
}

// you can pass those lists as parameters
private double[] apply(int index, List<Double> firstList, List<Double> secondList) {
    if (index < firstList.size() && index < secondList.size()) {
        if (index >= 0) {
            return new double[] { firstList.get(index), secondList.get(index) };
        }
    }
    return new double[] { OUT_OF_BOUNDS, OUT_OF_BOUNDS };
}

// you can even pass undefined number of lists (var-args)and grab there values at onnce
private double[] apply(int index, List<Double>... lists) {

    int listsSize = lists.length;

    if (index >= 0) {

        double[] search = new double[listsSize];

        for (int listIndex = 0; listIndex < listsSize; listIndex++) {

            List<Double> currentList = lists[listIndex];

            if (index < currentList.size()) {
                search[listIndex] = currentList.get(index);

            } else {
                search[listIndex] = OUT_OF_BOUNDS;
            }

        }
        return search;
    }

    double[] invalidSearch = new double[listsSize];
    for (int i = 0; i < listsSize; i++) {
        invalidSearch[i] = OUT_OF_BOUNDS;
    }
    return invalidSearch;

}

// now the work

public void combineLists() {
    initializeLists();
    double[] search = null;

    // search for index Zero in both lists
    search = apply(0);
    System.out.println(Arrays.toString(search));
    // result : [0.0, 500.0]

    // search for index One in both list parameters
    search = apply(1, firstList, secondList);
    System.out.println(Arrays.toString(search));
    // result : [1.0, 501.0]

    // search for index Two in var-args list parameters
    search = apply(2, firstList, secondList, thirdList);
    System.out.println(Arrays.toString(search));
    // result : [2.0, 502.0, 5002.0]


    // search for wrong index
    search = apply(800);
    System.out.println(Arrays.toString(search));
    // result : [-1.0, -1.0]

    // search for wrong index
    search = apply(800, firstList, secondList);
    System.out.println(Arrays.toString(search));
    // result : [-1.0, -1.0]


    // search for wrong index
    search = apply(800, firstList, secondList, thirdList);
    System.out.println(Arrays.toString(search));
    // result : [-1.0, -1.0,-1.0]

}
Ahmed Adel Ismail
  • 2,168
  • 16
  • 23
0

Here's one solution. Bounds checking and all error handling omitted:

public class Reducer
{
    public static <X, Y, Z> List<Z> reduce(List<X> list1, List<Y> list2, Function<X,Y,Z> func)
    {
        List<Z> result = new ArrayList<Z>();
        Iterator<X> i1 = list1.iterator();
        Iterator<Y> i2 = list2.iterator();
        for (int i=0; i<list1.size(); i++)
        {
            result.add(func.apply(i1.next(), i2.next()));
        }
        return result;
    }
}

public interface Function<X,Y,Z> {
    Z apply(X arg1, Y arg2);
}

And how to use it

public static void main(String[] args)
{
    Function<Double, Double, Double> f = new Function<Double, Double, Double>() {
        @Override
        public Double apply(Double a1, Double a2) { return a1 * a2; }
    };

    List<Double> d1 = new ArrayList<Double>();
    List<Double> d2 = new ArrayList<Double>();
    List<Double> result = Reducer.reduce(d1, d2, f);
}
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190