Reputation: 11963
I have a list of numbers like this:
[ 0, 1, 2, 3, 4, 5, 6, 7 ]
How to sum up every N (let's assume 2) elements in an elegant way and transform the list into:
[ 1, 5, 9, 13 ]
edit: I came up with the following solution:
List<Double> input = Arrays.asList(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0);
List<Double> output = new ArrayList<>();
int N = 2;
IntStream.range(0, (input.size() + N - 1) / N)
.mapToObj(i -> input.subList(i * N, Math.min(N * (i + 1), input.size())))
.mapToDouble(l -> l.stream().mapToDouble(Double::doubleValue).sum())
.forEach(output::add);
System.out.println(output);
It works, but I'm still looking for a more readable and simple one.
Upvotes: 3
Views: 4096
Reputation: 21124
Here's another approach which works despite your elements are sorted or not. Moreover it does not anticipate anything about your starting values and gives the correct result in a much broader spectrum. This approach still works even if the list.size() % n == 0
condition does not hold. So, literally, it does not need any preconditions to hold in order to get what is desired.
The intuition behind this approach is that your indices are naturally sorted, hence can be used to achieve what we need. We can group elements into one category as far as their current index i / n
yields the same value. Then a downstream collector is used to calculate the sum of elements that fall in the same group. Here's how it looks.
Collection<Integer> sumsOfnElements = IntStream.range(0, numbers.size()).boxed()
.collect(Collectors.groupingBy(i -> i / n,
Collectors.summingInt(numbers::get))).values();
Equivalent iterative solution following more or less same approach is given below. In a performance stringent setting, I would choose this imperative solution over the stream based one.
Assumption: input is an array of ints, Not a list.
final float inputLen = input.length;
final int resultLen = (int) Math.ceil(inputLen / n);
final int[] result = new int[resultLen];
for (int i = 0; i < inputLen; i++)
result[i / n] += input[i];
Upvotes: 2
Reputation: 393781
Here's an alternative approach, using Collectors.groupingBy()
:
List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7);
int N = 2;
Collection<Integer> sums = numbers.stream()
.collect(Collectors.groupingBy(i-> i/N,
Collectors.summingInt(Integer::intValue)))
.values();
System.out.println (sums);
Output:
[1, 5, 9, 13]
Upvotes: 2
Reputation: 8068
Given:
List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7);
int nth = 2;
How about:
IntStream.iterate(0, idx -> idx + nth)
.limit(numbers.size() / nth)
.map(idx -> IntStream.range(idx, idx + nth)
.reduce((sum, index) -> sum + numbers.get(index))
.orElse(0))
.forEach(System.out::println);
Or alternatively:
IntStream.range(0, numbers.size() / nth)
.map(idx -> IntStream.range(idx * nth, (idx + 1) * nth)
.map(index -> numbers.get(index))
.sum())
.forEach(System.out::println);
Upvotes: 2
Reputation: 1606
I think most elegant way is using flatMap, so you avoid O(n²) complexity. Here's an example:
public static class Helper {
private final int chunkSize;
private int index;
private int sum;
public Helper(int chunkSize) {
this.chunkSize = chunkSize;
}
public Stream<Integer> set(int v) {
index += 1;
sum += v;
ArrayList<Integer> ret = new ArrayList<>();
if (index == chunkSize) {
ret.add(sum);
sum = 0;
index = 0;
}
return ret.stream();
}
}
public static void main(String[] args) {
List<Integer> input = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7);
Helper helper = new Helper(2);
List<Integer> output = input.stream().flatMap(helper::set).collect(Collectors.toList());
System.out.println(input);
System.out.println(output);
}
This will outputs:
[0, 1, 2, 3, 4, 5, 6, 7]
[1, 5, 9, 13]
Upvotes: 0
Reputation: 120848
How about this? (I've edited to take nTh
)
int nTh = 2;
List<Integer> list = List.of(0, 1, 2, 3, 4, 5, 6, 7);
int[] result = IntStream.iterate(0, i -> i + nTh)
.limit(list.size() / nTh)
.map(x -> {
return IntStream.range(0, nTh).map(i -> list.get(i + x)).sum();
})
.toArray();
System.out.println(Arrays.toString(result));
Upvotes: 0