1

I'm trying to sort, by a single value, a collection of objects that themselves have objects for their values.

To be clear, I want to sort my results by the name string in each object's value object.

This is different from other questions (I believe... including the one that is listed above as "Possible duplicate...") in that I am sorting from a value that consists of an object of other values, not simply from a single value.

I have an object that looks like:

foo = {
  9876: {name: 'Banana', id:'875465'},
  4536: {name: 'Pear', id:'285610'},
  8732: {name: 'Apple', id:'013452'}
}

I want to sort that object, alphabetically, by the name value.

If I try to sort by Object.values the returned results are correctly sorted by the name value but thr return looks like:

[
  0: {name: 'Apple', id:'013452'}
  1: {name: 'Banana', id:'875465'}
  2: {name: 'Pear', id:'285610'}
]

What I need, however, is to retain my same collection of objects, just sorted properly. No array, no numeric keys... I just want foo as it was above, but sorted by name like:

foo = {
  8732: {name: 'Apple', id:'013452'},
  9876: {name: 'Banana', id:'875465'},
  4536: {name: 'Pear', id:'285610'}
}

How do I do that in Javascript?


For completeness, here is the sorting method I am currently attempting to use:

const sortStuff = (key) => {
  return (a, b) => {
    if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
      return 0;
    }

    const avalue = (typeof a[key] === 'string') ?
      a[key].toLowerCase() : a[key];
    const bvalue = (typeof b[key] === 'string') ?
      b[key].toLowerCase() : b[key];

    let comp = 0;
    if (avalue > bvalue) {
      comp = 1;
    }
    if (avalue < bvalue) {
      comp = -1;
    }

    return comp
  }
}

and I consume that like:

Object.values(foo).sort(sortStuff('name'))
  • Possible duplicate of [Sort JavaScript object by key](https://stackoverflow.com/questions/5467129/sort-javascript-object-by-key) – Jordan Running Aug 27 '18 at 20:50
  • you might rename the keys to ones with leading zero, because keys who could be indices (of an array) are sorted by value to top for access in order. – Nina Scholz Aug 27 '18 at 20:50
  • 2
    [Order of properties in a js object is not guaranteed](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order), so if you don't want to return an array then maybe look into a map. – benvc Aug 27 '18 at 21:02
  • @JordanRunning This is not the same as I am sorting by a value buried in an object, not by a key. Additionally, there is some confusion around if you can even sort an object (which is a claim in the link you provided) as you can see by one of the other comments here. – Stephen Thracian Aug 27 '18 at 21:42
  • This seems like an [XY problem](https://meta.stackexchange.com/a/66378/140350). _Why_ do you want to do this sorting? What problem are you trying to solve? – Jordan Running Aug 27 '18 at 21:44
  • @JordanRunning I have results that list those names. Currently I list them in the order they exist in the object. I want to sort that order so that it is output in an alphabetical order. – Stephen Thracian Aug 27 '18 at 21:48
  • Okay. The usual (and IMO correct) solution to that is to put them in an ordered data structure, i.e. an array. – Jordan Running Aug 27 '18 at 21:49
  • Then my existing solution will suffice. OK, thanks for validating that. I would upvote you if I could. – Stephen Thracian Aug 27 '18 at 21:51

1 Answers1

0

First what you need to do is create a custom sorting function. After this step then loop through the object and compare each element with all the other elements. If one element is found to be out of place then swap the element. It is best with an example.

// Current data set
foo = {
  9876: {name: 'Banana', id:'875465'},
  4536: {name: 'Pear', id:'285610'},
  8732: {name: 'Apple', id:'013452'}
}

// Custom compare function
function objectSorter(a, b){
    if(!a.hasOwnProperty("name") || !b.hasOwnProperty("name")) {
       return 0;
    }
    var x = a.name.toLowerCase();
    var y = b.name.toLowerCase();
    if (x < y) {return -1;}
    if (x > y) {return 1;}
    return 0;
};

// Compare each element
for(var item in foo)
  {
    // with all the other elements in the object
    for(var next in foo)
      {
        // if the outer item is less than the inner item
        // then swap places.
        if(objectSorter(foo[item], foo[next]) == -1)
        {
          var temp = foo[item];
          foo[item] = foo[next];
          foo[next] = temp;
        }
      }
  }

// Print out the newly ordered data set
console.log(foo);

//My output

{…}
​    4536: Object { name: "Apple", id: "013452" }
    8732: Object { name: "Banana", id: "875465" }
    9876: Object { name: "Pear", id: "285610" }
    <prototype>: Object { … }

But you most likely don't want a sorting answer you want your code to work. From what I find with using your code is that it is going to strip the data out of the objects and put them into an array. This isn't what you want. This is why you would use my initial answer. But this is what I found with your code.

foo = {
  9876: {name: 'Banana', id:'875465'},
  4536: {name: 'Pear', id:'285610'},
  8732: {name: 'Apple', id:'013452'},
  3536: {name: 'Peach', id:'385610'},
  5732: {name: 'Grape', id:'513452'}
};

var sortStuff = (key) => {
  return (a, b) => {
    if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
      return 0;
    }
    var avalue = (typeof a[key] === 'string') ? a[key].toLowerCase()  : a[key];
    var bvalue = (typeof b[key] === 'string') ? b[key].toLowerCase()  : b[key];
    var comp = 0;
    if (avalue > bvalue) {
      comp = 1;
    }
    else if (avalue < bvalue) {
      comp = - 1;
    }
    return comp;
  }
};


var resultFoo = Object.values(foo).sort(sortStuff('name'));

console.log(resultFoo, foo);

//Notice that resultFoo is printed and foo is also printed.

(5) […]
​0: Object { name: "Apple", id: "013452" }
​1: Object { name: "Banana", id: "875465" }
​2: Object { name: "Grape", id: "513452" }
​3: Object { name: "Peach", id: "385610" }
​4: Object { name: "Pear", id: "285610" }
​length: 5
​
{…}
​3536: Object { name: "Peach", id: "385610" }
​4536: Object { name: "Pear", id: "285610" }
​5732: Object { name: "Grape", id: "513452" }
​8732: Object { name: "Apple", id: "013452" }
​9876: Object { name: "Banana", id: "875465" }
​

Object.values was not created to return objects but it was created to return an array.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values

This way you see that using Object.values is not an option where you loose data.

943A
  • 156
  • 8
  • You cannot guarantee object property order in Javascript. – connexo Aug 27 '18 at 21:31
  • It is not important if it is { name: "Apple", id: "013452" } or { id: "013452", name: "Apple" }, name will still be found. – 943A Aug 27 '18 at 21:34
  • I was talking about 4536, 8732 and 9876 - there is no way you can guarantee their order in an object. – connexo Aug 27 '18 at 21:35
  • I see what element you are talking about. The objectSorter ignores that element and only looks at the Name value and it lower cases it then it compares it. The objects can be any order. – 943A Aug 27 '18 at 21:39
  • Is the name property going to have numeric values in it? like {name:"200Apple"}? – 943A Aug 27 '18 at 21:50