How to iterate the split result array of a multiline string value in order to reformat certain lines / newlines?

Viewed 504

I have a string of data that has line feeds in the middle. For example:

"Product Name \n Product Color \n Product Quantity \n Product Location \n Product Size \n Product Power" 

The Amount of elements in the string could be infinite.

I need to replace the \n with >>>> \n but only when the line numbers are greater than 3 and not the last line. On the last line I need it to say (end)

I have tried map, and I can split and replace, but I'm having trouble iterating through the array to identify the lines that are to be modified. As well as joining them back up once all lines are modified or not.

Input is:

"Retract-A-Banner\n**Template:\nFull Color\nImprint Information:\nCL: 488 Special Ops Wing\n353 SOW Integrated Resilience Optimization Network\n"

Expected Output:

"Retract-A-Banner\n**Template:\nFull Color >>>> \nImprint Information: >>>> \nCL: 488 Special Ops Wing >>>> \n353 SOW Integrated Resilience Optimization Network(end)\n"
6 Answers

Split by \n and recreate your string with the conditions:

  1. if this is the last one so add (end)
  2. if line is greater than 3, add >>>> \n
  3. otherwise a simple \n will be fine

EDIT:

add a check whether the last string is \n, and remove that value from the array.

const str = "Retract-A-Banner\n**Template:\nFull Color\nImprint Information:\nCL: 488 Special Ops Wing\n353 SOW Integrated Resilience Optimization Network\n";

const splitted = str.split('\n');
let output = '';

if (splitted[splitted.length - 1] === '')
  splitted.length--;
  
for (let i = 0; i < splitted.length; i++) {
  if (i + 1 === splitted.length) {
    output += splitted[i] + ' (end)';
  } else if (i >= 2) {
    output += splitted[i] + ' >>>> \n';
  } else {
    output += splitted[i] + '\n';
  }
}

console.log(output);

Actually one does not necessarily need to choose an iterating approach for the .split(/\n/) operation's result array.

The next introduced approach instead works with a straightforward data access (for all the information needed) via ...

And the return value gets assembled on the spot (depending on the last line's value) utilizing ...

function parseNewlines(value) {

  const[ line1, line2, ...rest ] = String(value).split(/\n/);
  const lastLine = rest.pop();

  return [
    `${ line1 }\n${ line2 }\n${ rest.join(' >>>> \n') }`,

    (lastLine === '')
      && `(end)\n`
      || ` >>>> \n${ lastLine }(end)`,

  ].join('');
}

const originalTestValue = `Retract-A-Banner
**Template:
Full Color
Imprint Information:
CL: 488 Special Ops Wing
353 SOW Integrated Resilience Optimization Network
` // be aware of the last line.

const alteredTestValue = `Retract-A-Banner
**Template:
Full Color
Imprint Information:
CL: 488 Special Ops Wing
353 SOW Integrated Resilience Optimization Network` // be aware of the last line.

// be aware of the last line.
const expectedResult = `Retract-A-Banner\n**Template:\nFull Color >>>> \nImprint Information: >>>> \nCL: 488 Special Ops Wing >>>> \n353 SOW Integrated Resilience Optimization Network(end)\n`;

// be aware of the last line.
const expectedResultOfAlteredValue = `Retract-A-Banner\n**Template:\nFull Color >>>> \nImprint Information: >>>> \nCL: 488 Special Ops Wing >>>> \n353 SOW Integrated Resilience Optimization Network(end)`;

console.log(
  `OP's use case ...\n${ parseNewlines(originalTestValue) }`
);
console.log(
  `altered use case ...\n${ parseNewlines(alteredTestValue) }`
);
console.log('\n');

console.log(
  "OP's use case ...\ntest passed ?..",
  (parseNewlines(originalTestValue) === expectedResult)
);
console.log('\n');

console.log(
  "altered use case ...\ntest passed ?..",
  (parseNewlines(alteredTestValue) === expectedResult)
);
console.log(
  "altered use case and altered expectation ...\ntest passed ?..",
  (parseNewlines(alteredTestValue) === expectedResultOfAlteredValue)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Nevertheless an iterating approach could be implemented with a classic reduce task.

function parseNewlines(value) {
  return String(value)

    .split(/\n/)
    .reduce((result, line, idx, arr) => {
      return [

        result,
        (idx < (arr.length - 1))

          ? `${ (idx >= 3) && ' >>>> ' || '' }\n${ line }`
          : (line === '')
              && `(end)\n`
              || ` >>>> \n${ line }(end)`,

      ].join('');
    });
}

const originalTestValue = `Retract-A-Banner
**Template:
Full Color
Imprint Information:
CL: 488 Special Ops Wing
353 SOW Integrated Resilience Optimization Network
` // be aware of the last line.

const alteredTestValue = `Retract-A-Banner
**Template:
Full Color
Imprint Information:
CL: 488 Special Ops Wing
353 SOW Integrated Resilience Optimization Network` // be aware of the last line.

// be aware of the last line.
const expectedResult = `Retract-A-Banner\n**Template:\nFull Color >>>> \nImprint Information: >>>> \nCL: 488 Special Ops Wing >>>> \n353 SOW Integrated Resilience Optimization Network(end)\n`;

// be aware of the last line.
const expectedResultOfAlteredValue = `Retract-A-Banner\n**Template:\nFull Color >>>> \nImprint Information: >>>> \nCL: 488 Special Ops Wing >>>> \n353 SOW Integrated Resilience Optimization Network(end)`;

console.log(
  `OP's use case ...\n${ parseNewlines(originalTestValue) }`
);
console.log(
  `altered use case ...\n${ parseNewlines(alteredTestValue) }`
);
console.log('\n');

console.log(
  "OP's use case ...\ntest passed ?..",
  (parseNewlines(originalTestValue) === expectedResult)
);
console.log('\n');

console.log(
  "altered use case ...\ntest passed ?..",
  (parseNewlines(alteredTestValue) === expectedResult)
);
console.log(
  "altered use case and altered expectation ...\ntest passed ?..",
  (parseNewlines(alteredTestValue) === expectedResultOfAlteredValue)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

How about something like this:

var data =
  "Retract-A-Banner\n**Template:\nFull Color\nImprint Information:\nCL: 488 Special Ops Wing\n353 SOW Integrated Resilience Optimization Network\n";

function convertToLines(entry) {
  return entry.split("\n").filter((val) => val);
}

function constructOutput(lines) {
  const numLines = lines.length;
  return lines.reduce((result, entry, index) => {
    var separator = index > 2 ? " >>>> \n" : "\n";
    if (index == numLines - 1) {
      separator = "(end)\n";
    }
    return result + entry + separator;
  }, "");
}
console.log(constructOutput(convertToLines(data)));

The convertToLines uses the filter to dump empty entries which you get from the new line at the end of the string. And then we have to tack that on to the (end) separator`.

  • Convert string into an array of strings

     string.split(/\n/)
    
  • Then use .flatMap() and a chained ternary as callback. Each condition is based on index

      idx < 3 ? [str+' \n']
      :
      idx === arr.length -1 ? [str+' (end)'] 
      : 
      [str+' >>>> \n']
    
  • Then .join('') the array back into a string

let test = `Retract-A-Banner\nTemplate:\nFull Color\nImprint Information:\nCL: 488 Special Ops Wing\n353 SOW Integrated Resilience Optimization Network\n" Expected Output:"Retract-A-Banner\nTemplate:\nFull Color \nImprint Information: \nCL: 488 Special Ops Wing \n353 SOW Integrated Resilience Optimization Network`;

const formatLines = string => string.split(/\n/).flatMap((str, idx, arr) => idx < 3 ? [str + ' \n '] : idx === arr.length - 1 ? [str + ' (end) '] : [str + ' >>>> \n ']).join('');

console.log(formatLines(test));

It seems like you want something like this:

var strings = `Retract-A-Banner\nTemplate:\nFull Color\nImprint Information:\nCL: 488 Special Ops Wing\n353 SOW Integrated Resilience Optimization Network\n`.split("\n");
strings.forEach((string,index) => {
    if (index > 2) {
       strings[index] = `>>>>\\n${string}`
    } else {
       index > 0 ? `\\n${string}` : strings[index];
    }
})
var finalString = strings.join("")+"(end)";
console.log(finalString);

Note that I'm escaping your \n in that code with an extra backslash. I cannot tell if that's what you want though.

To insert the >>>> you can use the regex: (?<=(.+\n.+){2,})(?=\n.)

  • The (?<=(.+\n.+){2,}) creates a positive lookbehind for three lines
  • The (?=\n.) creates a positive lookahead for a newline followed by any character

And then you can use the similar regex (?<=.)(?=\n*$) to add '(end)' to the last line.

const input = "Retract-A-Banner\n**Template:\nFull Color\nImprint Information:\nCL: 488 Special Ops Wing\n353 SOW Integrated Resilience Optimization Network\n";

const output = input
                .replace(/(?<=(.+\n.+){2,})(?=\n.)/g, ' >>>> ')
                .replace(/(?<=.)(?=\n*$)/, '(end)');

console.log(input);
console.log(output);


If you are using a literal backslash followed by a 'n' in your string, it would be formatted as \\\n in your JS string literal.

In that case you can use the regex (?<=(.+\\n.+){2,})(?=\\n.) to insert the >>>> and the regex (?=\\n$) for the (end).

const input = "Retract-A-Banner\\n**Template:\\nFull Color\\nImprint Information:\\nCL: 488 Special Ops Wing\\n353 SOW Integrated Resilience Optimization Network\\n";

const output = input
                .replace(/(?<=(.+\\n.+){2,})(?=\\n.)/g, ' >>>> ')
                .replace(/(?=\\n$)/, '(end)');

console.log(input);
console.log(output);

Related