Reputation: 157
I have the following JavaScript array of objects:
[{name: '4 Arn', isLetter: false},
{name: 'Abax', isLetter: false},
{name: 'Aramex', isLetter: false},
{name: 'Booking', isLetter: false},
{name: 'Dangerous', isLetter: false},
{name: 'Manali', isLetter: false}]
In the above array, I want to check the first letter of each item's name
property. If it matches, I want to append a new object just before the object, as shown in the following examples:
[{letter: "#", isLetter: true}, // new object
{name: '4 Arn', isLetter: false},
{letter: "A", isLetter: true}, // new Object
{name: 'Abax', isLetter: false},
{name: 'Aramex', isLetter: false},
{letter: "B", isLetter: true}, // new object
{name: 'Booking', isLetter: false},
{letter: "D", isLetter: true}, // new object
{name: 'Dangerous', isLetter: false},
{letter: "M", isLetter: true}, // new object
{name: 'Manali', isLetter: false}]
I tried the reduce()
function, but I don't understand why it's giving me the wrong result:
var newArr = [];
list.reduce(function(prev, cur, index, originalArrray) {
var previousCharcode = prev.name.toUpperCase().charCodeAt(0);
currentCharCode = cur.name.toUpperCase().charCodeAt(0);
newArr.push(prev);
if(previousCharcode != currentCharCode) {
newArr.splice(index, 0, {isLetter: true, letter: String.fromCharCode(currentCharCode)});
}
return cur;
});
Upvotes: 0
Views: 5488
Reputation: 26181
I guess you can also do in a functional way like this;
var arr = [{name: '4 Arn', isLetter: false},
{name: 'Abax', isLetter: false},
{name: 'Aramex', isLetter: false},
{name: 'Booking', isLetter: false},
{name: 'Dangerous', isLetter: false},
{name: 'Manali', isLetter: false}],
table = arr.reduce((p,c) => {var firstLetter = c.name[0];
isNaN(+firstLetter) ? p[firstLetter] ? p[firstLetter].push(c)
: p[firstLetter] = [c]
: p["#"] ? p["#"].push(c)
: p["#"] = [c];
return p;
},{}),
result = Object.keys(table).reduce((p,k) => p.concat({letter: k, isLetter: true},table[k]),[]);
console.log(result);
Hints : +"A" returns NaN but +"4" returns 4 as number. So isNaN()
is a very useful function to check the type.
Here is the version with conventional functions instead of arrows;
var arr = [{name: '4 Arn', isLetter: false},
{name: 'Abax', isLetter: false},
{name: 'Aramex', isLetter: false},
{name: 'Booking', isLetter: false},
{name: 'Dangerous', isLetter: false},
{name: 'Manali', isLetter: false}],
table = arr.reduce(function(p,c){
var firstLetter = c.name[0];
isNaN(+firstLetter) ? p[firstLetter] ? p[firstLetter].push(c)
: p[firstLetter] = [c]
: p["#"] ? p["#"].push(c)
: p["#"] = [c];
return p;
},{}),
result = Object.keys(table).reduce(function(p,k){
return p.concat({letter: k, isLetter: true},table[k]);
},[]);
console.log(result);
Upvotes: 1
Reputation: 350667
There are at least two reasons why your code does not give the expected result:
The index you work with, points to the index in the original array. As your new array will have more elements, it makes no sense to use that index for a splice
on the new array. It will be pointing to the wrong place eventually;
You only push the prev
element, so the last element will never be pushed to the result array
I would suggest to use reduce
with a accumulated value (first argument of the callback) that will build up the final array. To remember the last letter object that was introduced, I will pair this accumulated value with that letter. So I'll work with an array that contains two elements:
Then the final result will be taken from the first element of that array, ignoring the second value.
I suggest not to work with character codes, but just with the characters. It saves you from converting the code back to a character.
Here is the code:
var list = [
{name: '4 Arn', isLetter: false},
{name: 'Abax', isLetter: false},
{name: 'Aramex', isLetter: false},
{name: 'Booking', isLetter: false},
{name: 'Dangerous', isLetter: false},
{name: 'Manali', isLetter: false}
];
var newArr = list.reduce(function(collect, cur, index, originalArrray) {
var currentChar = cur.name.toUpperCase().substr(0,1);
if (currentChar < 'A') currentChar = '#';
if (collect[1] != currentChar) {
collect[0].push({isLetter: true, letter: currentChar});
collect[1] = currentChar;
}
collect[0].push(cur);
return collect;
}, [[], null])[0];
// output
console.log(newArr);
Upvotes: 4
Reputation: 817
Maybe with this approach you can resolve the problem
var list = [{name: '4 Arn', isLetter: false},
{name: 'Abax', isLetter: false},
{name: 'Aramex', isLetter: false},
{name: 'Booking', isLetter: false},
{name: 'Dangerous', isLetter: false},
{name: 'Manali', isLetter: false}];
var listResult = [];
list.map(function(item, index) {
if(index > 0) {
var currentCharcode = item.name.toUpperCase().charCodeAt(0);
var previousCharcode = list[index-1].name.toUpperCase().charCodeAt(0);
if(previousCharcode != currentCharcode) {
listResult.push({isLetter: true, letter: String.fromCharCode(currentCharcode)});
}
} else {
listResult.push({isLetter: true, letter: String.fromCharCode(currentCharcode)});
}
listResult.push(item);
});
console.log(JSON.stringify(listResult));
Upvotes: 1
Reputation: 9561
Check this solution. Iterate the array and append it to a new array.
var names = [{
name: '4 Arn',
isLetter: false
}, {
name: 'Abax',
isLetter: false
}, {
name: 'Aramex',
isLetter: false
}, {
name: 'Booking',
isLetter: false
}, {
name: 'Dangerous',
isLetter: false
}, {
name: 'Manali',
isLetter: false
}];
var newNames = [];
for (var i in names) {
var char = names[i].name.substring(0, 1);
var isNumber = !isNaN(char);
var entry = {
letter: (isNumber ? '#' : char.toUpperCase()),
isLetter: isNumber
};
newNames.push(entry);
newNames.push(names[i]);
}
console.log(newNames);
Upvotes: 1