Search code examples
javascripttypescriptlodash

Inserting Object Into Array Alphabetically From Nested Key Value


My app makes a http call and returns an array of objects. I need to loop over the objects and insert in to a new array in alphabetical order. Problem is the last item in the newly created array is the only item out of order.

Example code: https://stackblitz.com/edit/typescript-zh16xc?file=package.json,index.ts

Aware i can use the native .sort(), however i am opting to forEach because i am performing more operations on each item, but the example has been trimmed down.

Code:

import { findIndex } from 'lodash';

let myList = [];

mockHttp()
  .then((res: Array<any>) => {
    console.log(res)
    res.forEach(item => {
      let index = findIndex(myList, function(listItem) { 
        return listItem.name.toLowerCase() > item.name.toLowerCase(); 
      });

      myList.splice(index, 0, {
        name: item.name,
        id: item.id,
        enabled: false
      })
    })

    console.log('result', myList);
  });

function mockHttp() {
   let p = new Promise(function(resolve,reject){
      resolve([
        {
          id: 'dscjidovvocsi', name: 'Pear'
        },
        {
          id: 'dscjidovvocsi', name: 'Banana'
        },
        {
          id: 'dscjidovvocsi', name: 'Orange'
        },
        {
          id: 'dscjidovvocsi', name: 'Blueberry'
        },
        {
          id: 'dscjidovvocsi', name: 'Grapefruit'
        },
        {
          id: 'dscjidovvocsi', name: 'Peach'
        },
        {
          id: 'dscjidovvocsi', name: 'Strawberry'
        },
        {
          id: 'dscjidovvocsi', name: 'Dragonfruit'
        },
        {
          id: 'dscjidovvocsi', name: 'Mango'
        },
        {
          id: 'dscjidovvocsi', name: 'Apples'
        }            
      ]);
   });

   return p;
 }

The last item is: { enabled: false, id: "dscjidovvocsi", name: "Pear" }

Which is incorrect, all other items are ordered as expected.


Solution

  • findIndex will return -1 if can't find a match.

    When -1 is passed as an index to splice, it inserts that in the second to last place, since negative indices count from the end, rather than the start.

    const arr = [1,2,3]
    arr.splice(-1, 0, 4)
    console.log(arr) // [1,2,4,3]

    So you can fix it by detecting that case and making sure to insert it at the end instead with:

    if (index === -1) index = myList.length
    

    For example:

    mockHttp()
          .then((res: Array<any>) => {
            console.log(res)
            res.forEach(item => {
              let index = findIndex(myList, function(listItem) { 
                return listItem.name.toLowerCase() > item.name.toLowerCase(); 
              });
              if (index === -1) index = myList.length // added
    
              myList.splice(index, 0, {
                name: item.name,
                id: item.id,
                enabled: false
              })
            })
    
            console.log('result', myList);
          });
    

    FYI: I debugged this with this snippet in the loop:

    console.log('inserting', item.name, 'at index', index, 'into', myList.map(v => v.name))
    

    Which made it pretty clear what was going on when it logged this:

    debug results