1

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.

1
  • Did you try out some of the answers? Consider giving some feedback greetings! Commented Mar 9, 2021 at 20:38

3 Answers 3

1

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}

Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.