1

Context

  • I have five fields of fixed lengths that add up to 255 characters.
  • Lets call them split1, split2, split3, split4, split5
  • split1 -> max 80 characters
  • split2 -> max 60 characters
  • split3 -> max 40 characters
  • split4 -> max 40 characters
  • split5 -> max 35 characters length
  • We have a field with a max character size of 255, lets call this myStr

Goals

  • Split the myStr field into the 5 split fields based on the closest whitespace
  • while splitting, we don't want to cut the string in the middle of a word

What I tried

String str = 'Aliquip nisi Duis incididunt minim, cupidatat, est id culpa. Mollit cupidatat, officia do tempor ex amet. Fugiat sint Excepteur Duis. Nostrud consectetur occaecat ea commodo, lorem, esse officia sunt. Dolore ullamco adipiscing sed.';


List<String> listStr = new List<String>{
    str.substring(0, Math.min(80, str.length())),
    str.substring(80, Math.min(80, str.length())),
    str.substring(140, Math.min(140, str.length())),
    str.substring(180, Math.min(180, str.length())),
    str.substring(220, Math.min(220, str.length()))
        };
System.debug('Str: ' + JSON.serializePretty(listStr));
System.debug('Str length: ' + str.length());

Current Output

[ "Aliquip nisi Duis incididunt minim, cupidatat, est id culpa. Mollit cupidatat, o", "", "", "", "" ]

Expected Output

[ "Aliquip nisi Duis incididunt minim, cupidatat, est id culpa. Mollit cupidatat, ", " officia do tempor ex amet. Fugiat sint Excepteur Duis. ", "Nostrud consectetur occaecat ea commodo,", "lorem, esse officia sunt. Dolore ullamco", " adipiscing sed." ]

Where i need help

  • how do i get to split based on the max character lenth of each field?
1
  • The key here is to find the last space (using String.lastIndexOf) in the required run of characters length; if you find one, split there. If you don't the best you can do is split the long word at the maximum length you need. You then remove this part of the string and process the remainder with the next required run of characters length and so on. NB: chat GPT gives a fair answer if you also tell it you want the code to be Salesforce Apex.
    – Phil W
    Commented Sep 25 at 7:10

2 Answers 2

0

You are doing some mistakes like -

This code str.substring(80, Math.min(80, str.length())), will be executed as str.substring(80, 80), because Math.min(80, str.length()) where str.length = 231 (as per the string you provided) will always return min number, so that's why you are getting blank strings.

You can try this -

String str = 'Aliquip nisi Duis incididunt minim, cupidatat, est id culpa. Mollit cupidatat, officia do tempor ex amet. Fugiat sint Excepteur Duis. Nostrud consectetur occaecat ea commodo, lorem, esse officia sunt. Dolore ullamco adipiscing sed.';

List<String> splitFields = new List<String>();
Integer currentPosition = 0;
Integer remainingLength = str.length();

for (Integer maxLength : new List<Integer>{80, 60, 40, 40, 35}) {

    Integer nextWhitespace = 0;
    Integer index = 0;
    Integer lastWhiteSpace = 0;
    do {
        lastWhiteSpace = nextWhitespace;
        nextWhitespace = str.indexOf(' ', index);
        index = nextWhitespace+1;
    } while(nextWhiteSpace <= maxLength + currentPosition);

    if (nextWhitespace > maxLength + currentPosition) {
        nextWhitespace = lastWhiteSpace;
    }

    String split = str.substring(currentPosition, nextWhitespace).trim();
    splitFields.add(split);
    currentPosition = nextWhitespace + 1;
    remainingLength -= split.length() + 1;
    
    if (remainingLength < maxLength) {
        break;
    }
}

// Add any remaining characters to the last split field
if (remainingLength > 0) {
    splitFields.add(str.substring(currentPosition).trim());
}

System.debug('Split Fields: ' + JSON.serializePretty(splitFields));

I hope this helps 🤟

0

I think I'd approach this from a different angle. Instead of starting with a target index and trying to find the nearest space before that, get the individual words and keep concatenating them back together until you would run over your field limits.

// Starting string
String str = 'Aliquip nisi Duis incididunt minim, cupidatat, est id culpa. Mollit cupidatat, officia do tempor ex amet. Fugiat sint Excepteur Duis. Nostrud consectetur occaecat ea commodo, lorem, esse officia sunt. Dolore ullamco adipiscing sed.';

// Split by whitespace to get individual words (with punctuation counting as 
//   part of a "word")
List<String> words = str.split(' ');

List<Integer> fieldLengthLimits = new List<Integer>{ 80, 60, 40, 40, 35 };

List<String> results = new List<String>();
Integer fieldIndex = 0;
List<String> currentWordList = new List<String>();
// Putting this into a variable so we aren't doing an array access every iteration
//   of the loop
Integer currentLimit = fieldLengthLimits[fieldIndex];
Integer currentCount = 0;

// Using an iterator here allows us to re-use a bit of logic when we reach the final
//   word.
Iterator<String> wordIterator = words.iterator();
while(wordIterator.hasNext()) {
    String word = wordIterator.next();
    Integer thisLength = word.length();
    currentCount += thisLength;

    // When we exceed the field length, it's time to emit a string and
    //   reset our state.
    // This is also pretty much what we want to do when we reach the final word
    if(currentCount > currentLimit || !wordIterator.hasNext()) {
        fieldIndex++;
        
        // Ternary for a bit of safety as we reach the end of the fields we want
        //   to populate
        currentLimit = fieldIndex < fieldLengthLimits.size() ? fieldLengthLimits[fieldIndex] : 0;
        currentCount = thisLength;
        
        // Not wild about having this check, but it keeps the "final word"
        //   logic inside of the loop
        if(!wordIterator.hasNext()) { currentWordList.add(word); }
        
        results.add(String.join(currentWordList, ' '));
        currentWordList.clear();
    }

    currentWordList.add(word);

    // Need to add one more to the current count to account for the space
    //   we'll be adding back in when we emit a string
    currentCount++;
}

System.debug(JSON.serialize(results));

Output, with extra newlines to help distinguish between list entries:

08:49:43:006 USER_DEBUG [67]|DEBUG|[
"Aliquip nisi Duis incididunt minim, cupidatat, est id culpa. Mollit cupidatat,",
"officia do tempor ex amet. Fugiat sint Excepteur Duis.",
"Nostrud consectetur occaecat ea commodo,",
"lorem, esse officia sunt. Dolore ullamco",
"adipiscing sed."
]

3
  • This is less efficient since string concatenation is relatively inefficient and causes a lot of garbage to be created.
    – Phil W
    Commented Sep 25 at 19:26
  • @PhilW It's a 255 character string... getting the "most optimal" approach would be micro-optimization imo. Opted for "easy to reason about" here.
    – Derek F
    Commented Sep 25 at 19:34
  • Depends on how many of these are being processed together. But to each their own.
    – Phil W
    Commented Sep 26 at 6:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .