Problem
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;
}
Solution
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 }]
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/>');
}
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
);
}
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;
}