2
\$\begingroup\$

I have an array mapped to make a typical result string separated by commas and with numbers a bit beautified with array.map(formatNumber).

Expected format is

number [quantity], number [quantity], number [quantity]

So

1234 [3], 1235 [2], 2234 [1]

My mapping function was like this:

function formatNumberSelected(number, i) {
    return number.number + " [" + number.amount() + "]" + (i % 5 === 4 ? ',<br />' : ', ');
}

But to avoid comma (or comma and <br />) in the last iteration now is:

function formatNumberSelected(number, i, array) {
  var toReturn = number.number + " [" + number.amount() + "]";
  if ((i + 1) < array.length) {
    if (i % 5 === 4) toReturn += ',<br />';
    else toReturn += ', ';
  }
  return toReturn;
}

There is a better way to make this code more readable? Seems like passing array to each iteration just to find the last index is a bit too much.

If not I find better this solution not using Array.prototype.map:

function formatNumberArray(array) {
  var SEPARATOR = '';
  var toReturn = '';
  for (var index in array) {
    toReturn += SEPARATOR + array[index].number + ' [' + array[index].amount() + ']';
    if (index % 5 === 4) SEPARATOR = ',<br />';
    else SEPARATOR = ', ';
  }
  return toReturn;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ You could store all number [quantity] in an array, then apply a join on this array with , . Then add <br /> after every fifth comma. \$\endgroup\$ – Toto Apr 11 '17 at 10:25
1
\$\begingroup\$

You have serval ways to achieve this task. You can pass the length of array as well. About the implementation you can use forEach method like this.

 function formatNumberArray(arr) {
        let output = "";
        arr.forEach((v, i, a) => {
            output += v.number + "[" + v.amount + "]" + ((i + 1) % a.length == 0 ? "" : i % 5 == 4 ? ',<br />' : ', ');
        });
        return output;
    }

or you can iterate the array to the last index - 1 and then simply add the last value

function formatNumberArray(arr) {
        let output = "";
        for (let i = 0; i < arr.length - 1; ++i) {
            output += arr[i].number + "[" + arr[i].amount + "]" + ((i % 5 === 4) ? ',<br />' : ', ');
        }
        return output + arr[arr.length - 1].number + "[" + arr[arr.length - 1].amount + "]"
    }

I assume that the array is something like this.

  let arr = [{ number: 123, amount: 24 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }, { number: 16, amount: 10 }]
\$\endgroup\$
1
\$\begingroup\$

I assume you need a <br/> on the last line as well but not the comma so I would separate the logic for the two:

function formatNumberSelected(number, i, array) {
  var toReturn = number.number + " [" + number.amount() + "]";
  if (i+1 < array.length)
    toReturn = toReturn + ',';
  toReturn = toReturn + (i+1 == array.length || i % 5 === 4) ? '<br/>' : '';
  return toReturn; 
}

An alternative might be:

function formatNumberArray(array) {
  var formatted = array.map( function(el) { 
        return number.number + " [" + number.amount() + "]" 
         } );
  var lines = [];
  while (formatted.length > 0)
    lines.push( formatted.splice(0,4).join(', ');
  return lines.join(', <br/>');
}
\$\endgroup\$
1
\$\begingroup\$

I would consider using array.reduce() for this. I would also consider explicit code paths for:

  • item at the end of the array, which would not need a comma, but may or may not need a line break (this is unclear in your original code - for my answer, I assume you would want one)
  • item at the end of the output line, which need end of line and comma
  • and all other items, which just need commas

For example:

function reduceNumberArray(numberArray) {
    var arrayLength = numberArray.length;
    return numberArray.reduce(
        function(str, number, index) {
            str = str + number.number + ' [' + number.amount() + ']';
            // is last element in array?
            if (index === arrayLength - 1) {
                return str + '<br />';
            }
            // is last element in chunk?
            if ((index + 1) % 5 === 0) {
                return str + ',<br />';
            }
            return str + ', '; 
        },
        ''
    );
}

Or, if you you want the function behavior to be configurable:

function reduceNumberArray(numberArray, chunkSize, glue, endOfLine, baseString) {
    chunkSize = chunkSize || 5;
    glue = glue || ',';
    endOfLine = endOfLine || '<br />';
    baseString = baseString || '';
    var arrayLength = numberArray.length;
    return numberArray.reduce(
        function(str, number, index) {
            str = str + number.number + ' [' + number.amount() + ']';
            // is last element in array?
            if (index === arrayLength - 1) {
                return str + endOfLine;
            }
            // is last element in chunk?
            if ((index + 1) % chunkSize === 0) {
                return str + glue + endOfLine;
            }
            return str + glue + ' '; 
        },
        baseString
    );
}

Or, in ES6:

function reduceNumberArray(
    numberArray,
    chunkSize = 5,
    glue = ',',
    endOfLine = '<br />',
    baseString = '',
) {
    const arrayLength = numberArray.length;
    return numberArray.reduce( (str, number, index) => {
            str = str + number.number + ' [' + number.amount() + ']';
            // is last element in array?
            if (index === arrayLength - 1) {
                return str + endOfLine;
            }
            // is last element in chunk?
            if ((index + 1) % chunkSize === 0) {
                return str + glue + endOfLine;
            }
            return str + glue + ' '; 
        },
        baseString
    );
}
\$\endgroup\$
1
\$\begingroup\$

Your formatNumberSelected needs to understand that the number is part of an array, there are other numbers, and depending on the position of this number, I need to format it differently.

Whoa, whoa, whoa. Slow down, you're trying to do too much.

formatNumberSelected() should format a number.

function formatNumberSelected(number) {
    return number.number + " [" + number.amount() + "]"
}

formatArray() could format an array of numbers and understand how to piece them together and know where in the array we are, etc.

function formatArray(numbers) {
    var result = "";
    var len = numbers.length;
    var separator = "";
    for( i = 0; i < len; i++ ){
        result += formatNumberSelected(numbers[i]);
        if( i == len-1 ){
            separator = "";
        }
        else if( i % 5 == 4 ){
            separator = ",<br />";
        } else {
            separator = ",";
        }            
        result += separator;
    }
    return result;
}

You could take it a step farther and create determineSeparator()

function determineSeparator(index, arrayLength){
    if( index == arrayLength-1 ){
        return "";
    }
    else if( index % 5 == 4 ){
        return ",<br />";
    } else {
        return ",";
    }
}

Then your simplified formatArray would be

function formatArray(numbers) {
    var result = "";
    var len = numbers.length;
    for( i = 0; i < len; i++ ){
        result += formatNumberSelected(numbers[i]);
        result += determineSeparator(i, len);
    }
    return result;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.