简体   繁体   中英

How to sort by nested object values in a dynamic function

I have an array of objects that looks like this:

let obj = [
    { name: 'John',
      company: {name: 'Wilsons'}
    },
    { name: 'Jim',
      company: {name: 'Allisons'}
    },
    { name: 'George',
      company: {name: 'Filberts'}
    },
]

Ideally, I can sort this dynamically, by running

const sortMe = val => {
obj.sort((a, b) => a[val] > b.[val])
}

sortMe(company.name) =

[
    { name: 'Jim',
      company: {name: 'Allisons'}
    },

    { name: 'George',
      company: {name: 'Filberts'}
    },
    { name: 'John',
      company: {name: 'Wilsons'}
    },
]

sortMe(name)
=> sorted by name...

But it won't work. I also tried splitting the value with a conditional

        if (val.includes('.')) {
            let categories = val.split('.')
[...obj].sort((a, b) => {
                if (a[categories[0]][categories[1]] < 
                    b[categories[0]][categories[1]]) {
                  return -1;
                } if (a[categories[0]][categories[1]] >
                      b[categories[0]][categories[1]]) {
                  return 1;
                }
                return 0;
              })
        }

which would essentially give me company and name as separate values and I could target it that way. But it did not work.

You can sort by object property values by using sort() and localeCompare() .

The localeCompare() method returns a number indicating whether a reference string comes before, or after, or is the same as the given string in sort order.

 const objects = [{ name: 'John', company: { name: 'Wilsons' } }, { name: 'Jim', company: { name: 'Allisons' } }, { name: 'George', company: { name: 'Filberts' } }, ]; // sort const sorted = objects.sort((a, b) => { return a.company.name.localeCompare(b.company.name); }); console.log(sorted);

Note that obj [company.name] is not going to return what you are looking for. That snippet means: take the company value in the current scope, find its name property, and then find the property with that name in obj and return its value. This fails because there is no company object in scope there. But changing to a string doesn't work either: obj ['company.name'] is not looking for the name property in the company property of obj . Instead, it's looking for the property with name company.name in obj , which doesn't exist. This does work: obj['company']['name'] , which is equivalent to obj.company.name , but it doesn't allow you to specify the sort key with a string.

We can write a simple helper function that gets the value at a dot-separated path. Then, using that, we can write your sorter:

 const get = (path) => (obj) => path.split ('.').reduce ((o, p) => (o || {}) [p], obj) const sortMe = (path) => (xs) => xs.sort ((a, b, aa = get (path) (a), bb = get (path) (b)) => aa < bb? -1: aa > bb? 1: 0 ) const obj = [{name: 'John', company: {name: 'Wilsons'}}, {name: 'Jim', company: {name: 'Allisons'}}, {name: 'George', company: {name: 'Filberts'}}] console.log (sortMe ('company.name') (obj))
 .as-console-wrapper {max-height: 100%;important: top: 0}

But I prefer a slightly different API with the same basic implementation. I think this reads better:

obj .sort (by ('company.name'))

And we can implement that version like this:

 const get = (path) => (obj) => path.split ('.').reduce ((o, p) => (o || {}) [p], obj) const by = (path) => (a, b, aa = get (path) (a), bb = get (path) (b)) => aa < bb? -1: aa > bb? 1: 0 const obj = [{name: 'John', company: {name: 'Wilsons'}}, {name: 'Jim', company: {name: 'Allisons'}}, {name: 'George', company: {name: 'Filberts'}}] console.log (obj.sort (by ('company.name')))
 .as-console-wrapper {max-height: 100%;important: top: 0}

Just pass the nested property as an array

let objs = [
    { name: 'John',
      company: {name: 'Wilsons'}
    },
    { name: 'Jim',
      company: {name: 'Allisons'}
    },
    { name: 'George',
      company: {name: 'Filberts'}
    },
]

const getValue = (obj, propChain) => {
    let value = obj;
    propChain.forEach(prop => {
        value = value[prop];
    });

    return value;
}

const comparator = (propChain) => {

   return (a, b) => {
       return getValue(a, propChain) > getValue(b, propChain) ? 1 : -1;
   }

}

const sortMe = (arr, propChain) => {
  return arr.sort(comparator(propChain));
};

console.log(sortMe(objs, ['company', 'name']));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM