0

I have a list like : lst = {"a","b","c","b","c","a","c","a","b"}

I want to divide this in chunks :

like first chunk : a,b,c second is : b,c,a third is : c,a,b

Please note these chunks can be in any order and are not the simple String object, but can be anything (Viz: Complex object, Map etc).

Also I cannot break into chunks blindly since data in each chunk should be same and its a related data ie: in first chunk a,b,c are interrelated..likewise in second chunk b,c,a are interrelated and so on.

How can I do that.

One possible way I was thinking was to use collectors and groups by in Java8.

Any suggestions.

PS: Please ignore the syntax, this is just for understanding purpose.

Note: The answer for which it is marked duplicate here does not apply.

Lovey
  • 880
  • 3
  • 15
  • 31
  • 4
    What if list had 7 values? How would you chunk that? What is your criteria? Number of chunks or size of chunks? – Andreas Aug 30 '16 at 05:43
  • 3
    grouping based on what? `list.size()/n`? – Deendayal Garg Aug 30 '16 at 05:43
  • 1
    @Lovey Starting from usual code for splitting your array into groups (the last of which may be incomplete, i.e., with fewer elements) and combining it with Java8 concepts such as stream (which aren't covered in duplicate question, though), you could use an IntStream covering indexes of all fragments needed, then map each index to a sub-array, as in: `int stripe= 3;` `String[][] stripes = IntStream.range(0, (lst.length + stripe - 1) / stripe).mapToObj(i -> Arrays.copyOfRange(lst, i*stripe, Math.min(lst.length, (i + 1) * stripe))).toArray(String[][]::new);` – logtwo Aug 30 '16 at 07:20
  • 1
    ... whereas, if you are using an actual `List`, rather than a `String[]`, you could resort to `List.subList()`, such as in: `List realList = Arrays.asList(lst);` `Collection> listStripes = IntStream.range(0, (lst.length+stripe-1)/stripe).mapToObj(i -> realList.subList(i*stripe, Math.min(lst.length, (i+1)*stripe))).collect(Collectors.toList());` Drawbacks: inner `List` are just views onto the initial `List`, so modifying the latter **will** change the former. Beware! Advantages: creating such fragments don't require copying arrays. Faster! – logtwo Aug 30 '16 at 07:27
  • 1
    @Makoto, could you please check the question marked as a duplicate? I couldn't see any reference to stream/java8 there, just an algorithm for splitting into equal-sized (except last) fragments, which is clearly key here (thus is a very good reference), but possibly that is not enough to answer to Lovey's question. – logtwo Aug 30 '16 at 07:32
  • 1
    @logtwo: In the interim, [someone *did* add an answer which involves Java 8 and streams](http://stackoverflow.com/a/39220959/1079354). My gut feeling on this though is that the Stream API isn't necessary to solve this sort of problem, since it's just chunking out a list into different divisions. Understanding the root approach would be more valuable than just using the Stream API to solve it, since the solutions *would* still be about equivalent. – Makoto Aug 30 '16 at 07:49
  • @Makoto now I have checked it out that answer, thanks. I agree with you that root approach is key, however any stream-based solution _has_ an (obvious) advantage of its own: just by stripping the final `collect` you could complete the chain with more steps and achieve a computation more complex without having to materialize any intermediate fragment object (array or list). In a sense, using int stream ranges is just the first building block. Anyway, now we have plenty of those stream-based solutions to show this, both here and there, so everything is fine! Thank you :-) – logtwo Aug 30 '16 at 08:00

1 Answers1

1

I don't see a posibility to do this without some intermediate mapping object. Just for trying it out, I came up with the following solution:

public class Try30 {
    public static class StringIntPair {
        public final int i;
        public final String s;
        public StringIntPair(int i, String s) {
            this.i = i;
            this.s = s;
        }
    }

    public static void main(String[] args) {
        List<String> in = Arrays.asList("a","b","c","b","c","a","c","a","b");
        AtomicInteger cnt = new AtomicInteger(0);
        Map<Integer, List<String>> res = in.stream()
            .map(s -> new StringIntPair(cnt.getAndIncrement(), s))
            .collect(Collectors.groupingBy(
                p -> p.i / 3,
                Collectors.mapping(p -> p.s, Collectors.toList())
                ));
        System.out.println(res);
    }
}

However, in real life I'd rather go for a classic loop approach, as this seems totally convoluted and extremely hard to understand. Please do not forget, that even in Java 8 for-loops and while-loops are still a supported and valuable language feature.

mtj
  • 3,381
  • 19
  • 30