Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort a list by property and add an object before each first letter changes in JavaScript

So I am trying to make a UI like this:

enter image description here

And I have an array of users

[{name: 'Julia'}, {name: 'Ismeh'}, {name: 'Alison'}, {name: 'Andrea'}, {name: 'Betty'}]

What I am trying to do is to sort the array by first letter of the name property, and add a header object before each. For example in the picture, you can see the letter A, B, I, and J as the headers.

For now, I got it working like this:

    let final = []
    // sort by first letter
    const sortedUsers = state.test_list.sort((a, b) => a.name.localeCompare(b.name))
    for (let x = 0; x < sortedUsers.length; x++) {
        const user = sortedUsers[x].name
        if (user.charAt(0) === 'A') {
            const checkIfExists = final.findIndex((f) => f.header === 'A')
            // add the header A if it doesn't exist
            if (checkIfExists < 0) final.push({header: 'A'})
        }
        else if (user.charAt(0) === 'B') {
            const checkIfExists = final.findIndex((f) => f.header === 'B')
            // add the header B if it doesn't exist
            if (checkIfExists < 0) final.push({header: 'B'})
        }
        // else if up to the letter Z
        final.push(user)
    }

and if I log the final array, I get:

enter image description here

which is correct.

My concern is that the code is very long, and I have no idea if it can be optimized or make the code smaller.

Is there any other option to do something like this? Any help would be much appreciated.

like image 466
wobsoriano Avatar asked Mar 06 '23 13:03

wobsoriano


2 Answers

Why don't you create a collection of names, which is grouped by the first letter? You can then loop on it, and create your list. Use Array#reduce to create the grouped collection.

And then use Object#keys to iterate over the grouped collection and render your results:

let data = [{
  name: 'Julia'
}, {
  name: 'Ismeh'
}, {
  name: 'Alison'
}, {
  name: 'Andrea'
}, {
  name: 'Betty'
}];

let combined = data.reduce((result, item) => {
  let letter = item.name[0].toUpperCase();
  if (!result[letter]) {
    result[letter] = [];
  }

  result[letter].push(item);
  return result;
}, {});

console.log(combined);

// Iterate over the result
Object.keys(combined).forEach(key => {
  // key will be the first letter of the user names and
  // combined[key] will be an array of user objects
  console.log(key, combined[key]);
});

One thing still to do is to sort the user arrays by user name, which you can do easily using Array#sort.

like image 179
31piy Avatar answered Mar 28 '23 11:03

31piy


Simple enough, try sorting them and then using .reduce:

const unsortedPeople = [{name: 'Julia'}, {name: 'Ismeh'}, {name: 'Alison'}, {name: 'Andrea'}, {name: 'Betty'}];
const sortedUsers = unsortedPeople.sort((a, b) => a.name.localeCompare(b.name))
const final = sortedUsers.reduce((finalSoFar, user) => {
  const thisUserFirstChar = user.name[0];
  if (finalSoFar.length === 0) addHeader();
  else {
    const lastUserFirstChar = finalSoFar[finalSoFar.length - 1].name[0];
    if (lastUserFirstChar !== thisUserFirstChar) addHeader();
  }
  finalSoFar.push(user);
  return finalSoFar;
  function addHeader() {
    finalSoFar.push({ header: thisUserFirstChar });
  }
}, []);

console.log(final);
like image 43
CertainPerformance Avatar answered Mar 28 '23 11:03

CertainPerformance