0

I'm trying to pass a variable and use it as a literal attribute to sort my data, however, when the variable has more than one level with dot notation the localeCompare errors with undefined.

handleTableSort = (sortByColumn) => (event) => {
  event.preventDefault();

  const sortByColumn = 'name.first';

  const profileCandidates = this.state.profileCandidates.sort((a, b) => a[sortByColumn].localeCompare(b[sortByColumn]));

  this.setState({
   profileCandidates: profileCandidates
  })       
}
bp123
  • 3,217
  • 8
  • 35
  • 74
  • FYI - the dot in your string will not evaluate, as written your code will try to sort based on `a['name.first']`, not `a['name']['first']` or `a.name.first` – Rob M. Jan 03 '18 at 00:08
  • Yeh, if I change the variable to `const sortByColumn = '[name][first]'` I get the same result. How should it look? @RobM. – bp123 Jan 03 '18 at 00:15

2 Answers2

1

So when using bracket notation to access a property, javascript is going to look for that exact key. So by saying obj["name.first"], it's looking for that key literally, so it would match {"name.first": "bob"}, but not {name: {first: "bob"}}.

To get the behavior you want, you have to separate each part manually. So a simple function to get an arbitrary property key might be:

function getKeyAt(obj, key) {
    let result = obj;
    for (const part of key.split(".")) {
        if (!(part in result)) return; // If the key doesn't exist, return.
        result = result[part];
    }
    return result;
}

OR, if you have lodash available, it provides a handy function _.get to do this for you.

Once you have such a function, your code could be modified like so:

handleTableSort = (sortByColumn) => (event) => {
  event.preventDefault();

  const sortByColumn = 'name.first';

  const profileCandidates = this.state.profileCandidates.sort((a, b) => getKeyAt(a, sortByColumn).localeCompare(getKeyAt(b, sortByColumn));

  this.setState({
   profileCandidates: profileCandidates
  })       
}
CRice
  • 29,968
  • 4
  • 57
  • 70
0

So far as I know, there is not a native way to do this, but the code is easy enough:

const deref = (o, c) => {
    const _deref = (o, l) => l.length? _deref(o[l[0]], l.slice(1)): o;
    return _deref(o, c.split('.'));
};
...
  const profileCandidates = this.state.profileCandidates.sort((a, b)
       => a[sortByColumn].localeCompare(deref(b, sortByColumn)));
Michael Lorton
  • 43,060
  • 26
  • 103
  • 144