Wimal Weerawansa Wimal Weerawansa - 4 months ago 10
Javascript Question

Javascript array filter by alphabetic letter

I have

JavaScript
array of objects like this

[{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}]


above array I want to check each items
name
properties first letter and append new object just before the object.

[{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}]


and i tried with reduce() function but I dont't understand why its giving me 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;
});

Answer

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:

  • The final array being accumulated
  • The letter of the most recently added letter object

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);