-3

I'm attempting a block of code that I can't get quite right. I have a list with say, 1000 string entries (of 3 - 10 characters each) that I collect into a single comma-delimited string. If the total size of the characters in the resulting string is more than 8100, I need to split the list and create TWO comma-delimited strings (or 3, or whatever factor of 8100). I know this needs a groupingBy, but my syntax isn't working.

So my first question is, how can I determine the sum of the characters in my list, and how can I group the list such that there are no more than 8100 characters in each group of lists? This is what I have so far:

AtomicInteger counter = new AtomicInteger();
String codes = configInfos.stream()
    .map(ConfigInfo::getCode)
    .collect(Collectors.groupingBy(it -> counter.getAndIncrement() / 8100))
ernest_k
  • 44,416
  • 5
  • 53
  • 99
CNDyson
  • 1,687
  • 7
  • 28
  • 63
  • 1
    Does this answer your question? [Split string to equal length substrings in Java](https://stackoverflow.com/questions/3760152/split-string-to-equal-length-substrings-in-java) – knittl Apr 14 '20 at 17:42
  • 4
    "grouping by" is not really for "breaking up a data set into separate groups". `groupingBy` is for identifying subsets in your data set that have a common attribute of your choosing (usually for aggregating: like: count words by initial, where all initials in the word dataset determine a group each). In this case, I'd suggest you use a loop and an `int` variable that you can reset every time you reach 8100. You can also do something like this with a stream, but it's a little unnatural. – ernest_k Apr 14 '20 at 17:43
  • 1
    Do you count the chars after joining them with your delimeter, or before joininig them? In other words do you count the commas in the resulting strings? – Eritrean Apr 14 '20 at 17:48
  • I count the whole string after joining it. And yes, I include the comma. – CNDyson Apr 14 '20 at 18:25

2 Answers2

3

This needs to be solved with Streams?

Otherwise I would solve it like this:

String [] array = configInfos
    .stream()
    .map( ConfigInfo::getCode )
    .toArray( String []::new );
List<String> codes = new ArrayList<>();
StringJoiner joiner = new StringJoiner( "," );
for( String s : array )
{
    if( joiner.length() + 1 + s.length() > 8100 )
    {
        codes.add( joiner.toString() );
        joiner = new StringJoiner( "," );
    }
    joiner.add( s );
}
if( joiner.length() > 0 ) codes.add( joiner.toString() );

But I have to confess, I have no clue how to solve this with Streams …

Holger
  • 285,553
  • 42
  • 434
  • 765
tquadrat
  • 3,033
  • 1
  • 16
  • 29
  • This looks close!! I'd like to use Streams though for maintainability. – CNDyson Apr 14 '20 at 18:30
  • 2
    @user1660256 “use Streams for maintainability” makes no sense. Code doesn’t become more maintainable just because you’re using the Stream API. – Holger Apr 15 '20 at 07:56
  • Streams are more considerate and readable. Makes perfect sense to me. – CNDyson Apr 15 '20 at 11:41
  • @user1660256 You’re mistaken there. In some cases streams are are more readable. In this case they would make for quite unreadable code. There still is no silver bullet, and we must select our tools by the situation. The code in this answer is very elegant. – Ole V.V. Apr 15 '20 at 12:40
  • Even though this answer isn't syntactically correct, I'll accept it. – CNDyson Apr 15 '20 at 13:24
  • @Holger: I wrote the code on one machine and ten-finger-copied it here, in a hurry. That's a bad excuse, but I hope it is a good reason for the syntax bugs in the code. I apologise for that, and I fixed it according to your comment – hoping that I got it right now. – tquadrat Apr 15 '20 at 14:15
3

Edit: don’t use streams for this

I need the string maintained when I do the split. It has to look like the original list, with list 1 being "Entries", "are". List 2 would be "three", "upto". List 3 would be "ten", "chars", and so on.

Stream operations are not suited for your task. I recommend you use a classical loop for the clearest and easiest to maintain code.

Original answer: Intstream.range()

Not sure this is really what you want. In case you prefer to use a stream, here’s my attempt at that.

    final int maxSubstringLength = 9; // 8100

    List<String> entries = List.of("Entries", "are", "three", "upto", "ten", "chars", "each");
    String totalString = entries.stream().collect(Collectors.joining(","));

    // round up in the division
    int substringCount = (totalString.length() + maxSubstringLength - 1) / maxSubstringLength;
    List<String> substrings = IntStream.range(0, substringCount)
            .mapToObj(i -> totalString.substring(i * maxSubstringLength, Math.min((i + 1) * maxSubstringLength, totalString.length())))
            .collect(Collectors.toList());

    substrings.forEach(System.out::println);

Output:

Entries,a
re,three,
upto,ten,
chars,eac
h

For your very long string just put 8100 as max substring length where I put 9 for the demonstration.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • But I need the string maintained when I do the split. It has to look like the original list, with list 1 being "Entries", "are". List 2 would be "three", "upto". List 3 would be "ten", "chars", and so on. – CNDyson Apr 14 '20 at 18:28