0

A common problem that i have when solving my programming homework is that i have two arrays a,b and i want to combine them using a function a x b -> c so that c.

For example if:

a = {1,2,3}
b = {3,4,5}
f = (i1, i2) -> i1 + i2

c should be

c = {4,6,8}

The best solution I could come up with is something like this:

public static <A,B,C> Iterator<C> merge(Iterable<A> a, Iterable<B> b, BiFunction<A,B,C> f){
    Iterator<A> ai = a.iterator();
    Iterator<B> bi = b.iterator();
    return new Iterator<>() {

        @Override
        public boolean hasNext() {
            return ai.hasNext() && bi.hasNext();
        }

        @Override
        public C next() {
            return f.apply(ai.next(), bi.next());
        }
    };
}

but because java handles generics the way it handles generics I would also have to create specialized versions for primitive arrays (e.g. double[]), which amplifies the problem that I don't want to copy and paste like 100 lines of code every time I start a new homework.

Is there a standard library way of doing this?

edit: double[] version

public static double[] merge(double[] a, double[] b, DoubleBinaryOperator f){
    return IntStream.range(0, Math.min(a.length, b.length)).mapToDouble(i -> f.applyAsDouble(a[i], b[i])).toArray();
}
sidit77
  • 49
  • 5
  • What about Java Streams? – MC Emperor Jul 20 '19 at 22:15
  • I can't find a good solution using Streams other than the ugly IntStream version that requires me to save both arrays to temp variables. – sidit77 Jul 20 '19 at 22:20
  • Perhaps you need a new type -- `CoIterable`, one guaranteed to be of the same (or similar) type and collection length as another collection. – Hovercraft Full Of Eels Jul 20 '19 at 22:21
  • 1
    https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip could help – aschoerk Jul 20 '19 at 22:27
  • 3
    The concept that you're looking for is called `zip`. Experimental built-in support was removed before Streams went final, but Guava provides a function to do it, and other functional libraries such as Vavr provide the operation for their collections. – chrylis -cautiouslyoptimistic- Jul 20 '19 at 22:27
  • zip seems to be exactly what im looking for. I'm pretty disappointed that this seems to be another "the satisfying solution was so close, but java had to be java" problem that start to ruin java for me even though I really like java at its core. – sidit77 Jul 20 '19 at 23:02
  • Well, there's also the [`StreamEx`](https://github.com/amaembo/streamex) library, which contains additional steam operations. Maybe it contains such functionality. – MC Emperor Jul 21 '19 at 07:10

4 Answers4

0

How about


      int[] a = { 1, 2, 3
      };
      int[] b = { 3, 4, 5
      };

      int[] both = IntStream.range(0, a.length).mapToObj(n -> new int[] { a[n], b[n]
      }).flatMapToInt(r -> IntStream.of(r)).toArray();

In this example, the arrays must be the same length.

If you want to add corresponding elements, do this.


      int[] both = IntStream.range(0, a.length).map(n -> a[n] + b[n]).toArray();
      System.out.println(Arrays.toString(both));

WJS
  • 36,363
  • 4
  • 24
  • 39
0

Have a look at Guava Streams.zip : https://static.javadoc.io/com.google.guava/guava/21.0/com/google/common/collect/Streams.html#zip-java.util.stream.Stream-java.util.stream.Stream-java.util.function.BiFunction-

From the doc:

 Streams.zip(
   Stream.of("foo1", "foo2", "foo3"),
   Stream.of("bar1", "bar2"),
   (arg1, arg2) -> arg1 + ":" + arg2)

will return

Stream.of("foo1:bar1", "foo2:bar2"). 
David Soroko
  • 8,521
  • 2
  • 39
  • 51
0

Look at Streams.zip() from Guava. That'll handle the code for what you have already. Except you'll need to use Streams.stream(iterable) because Java iterable doesn't support streams directly.

To handle the primitives, just use IntStream or the equivalents.

public class MergingIterators {
  public static <A, B, C> Iterator<C> merge(
      Iterable<A> a, Iterable<B> b, BiFunction<A, B, C> f) {
    return Streams.zip(Streams.stream(a), Streams.stream(b), f);
  }

  public static Iterator<Integer> merge(
      int[] a, int[] b, BiFunction<Integer, Integer, Integer> f) {
    return Streams.zip(
        IntStream.of(a).boxed(), IntStream.of(b).boxed(), f);
  }
}
-1

So my problem is called zipping and there is a pretty good stackoverflow post here!. Unfortunately there seems to be no good solution, because I can't use libraries or even utility classes so I guess I have to stick with ugly workarounds.

sidit77
  • 49
  • 5