简体   繁体   中英

Sort an array of objects in Javascript by key value

First time poster, long time reader. I'm having a problem sorting an array of objects, this is homework so I'm not asking for someone to write the code for me just point me in the right direction or show me my over sight. The object is to write a function to sort an array of objects when passing in an array and a key ie:

([{a:2},{b:2},{a:1},{a:3},{b:3},{b:1}], “a”)

Should return

[{a:1},{a:2},{a:3},{b:1},{b:2},{b:3}];

I can't use anything like underscore.js or node.js

    //example array to use
    var testarr = [{a:2},{b:2},{a:1},{a:3},{b:3},{b:1}];
    console.log("array sort test: should look like [{a:1},{a:2},{a:3},{b:1},{b:2},{b:3}]");

    //first attempt 
    var sortArrayByKey = function (arr1, key) {
            (arr1.sort(function(a,b){
            return a[key] - b[key];}));
            return arr1;
    };
    //still only returns testarr
    console.log(sortArrayByKey(testarr, "a") );

    //second attempt
    var sortArrayByKey1 = function (array, key) {
    var compareByKey = function (a, b) {
            var x = a[key]; var y = b[key];
            return x - y;
    }
    array.sort(compareByKey);        
    return array;
    };
    //still only returns testarr
    console.log(sortArrayByKey1(testarr, “a”));

![pic of requirements in-case i'm describing it wrong photo

Hmm.. this is a weird one. First you need to check if one of the keys is the priority key and sort based on that. Then if both keys are equal sort by the values. The problem is that there is no straightforward way to get the key but you can use the for .. in loop.

I'm going to assume that each object contains only one property otherwise the code will not make sense since property is unordered in objects:

function sortPreferredKey(arr,key) {
    arr.sort(function(a,b){
        // get the keys of each object
        for (var a_key in a) {break}
        for (var b_key in b) {break}
        if (a_key != b_key) {
            if (a_key == key) return 1;
            else if (b_key == key) return -1;
            else return 0;
        }
        return a[a_key] - b[b_key];
    });
}

I may have gotten the order of sort wrong but you get the idea. It's really weird that you'd even need to do something like this.

Here's my solution. I made it so you can also add more keys and sort them too.

Fiddle - http://jsfiddle.net/Q7Q9C/3/

function specialSort(arrayToSort, keyOrder) {
    arrayToSort = arrayToSort.sort(function (a, b) {
        for (var key in keyOrder) {
            if (!keyOrder.hasOwnProperty(key)) {
                continue;
            }
            var aKey = keyOrder[key];
            if (typeof a[aKey] === "undefined" && typeof b[aKey] === "undefined") {
                continue;
            }
            if (typeof a[aKey] !== "undefined" && typeof b[aKey] === "undefined") {
                return -1;
            }
            if (typeof a[aKey] === "undefined" && typeof b[aKey] !== "undefined") {
                return 1;
            }
            if (a[aKey] > b[aKey]) {
                return 1;
            }
            else if (a[aKey] < b[aKey]) {
                return -1;
            }
        }
        return 0;
    });
    return arrayToSort;
}
var arrayToSort = [
    {a:2},
    {b:2},
    {a:1},
    {a:3},
    {b:3},
    {c:3},
    {c:2},
    {b:1}
];
var keyOrder = ["a", "b", "c"];
var sortedArray = specialSort(arrayToSort, keyOrder);
console.log(JSON.stringify(sortedArray));

This is the best I can come up with. It will sort all the elements with the given key to the front; the elements without the key will be in the back, but they'll be in an unpredictable order.

function sortArrayByKey(arr, key) {
    function compareKey(a, b) {
        if (a.hasOwnProperty(key)) {
            if (b.hasOwnProperty(key)) {
                return a[key] - b[key];
            } else {
                return -1;
            }
        } else if (b.hasOwnProperty(key)) {
            return 1;
        } else {
            return 0;
        }
    }
    arr.sort(compareKey);
    return arr;
}

Here's a solution that makes a guess what to do if neither object being compared in the array has the passed in comparison key:

var data = [{a:2},{b:2},{a:1},{a:3},{b:3},{b:1}];

function sortByProperty(array, propName) {

    function findFirstProperty(obj) {
        for (x in obj) {
            if (obj.hasOwnProperty(x)) {
                return x;
            }
        }
    }

    return array.sort(function(first, second) {
        var firstHasProp = propName in first;
        var secondHasProp = propName in second;
        if (firstHasProp) {
            if (secondHasProp) {
                // both have the property
                return first[propName] - second[propName];
            } else {
                // only first has the property
                return -1;
            }
        } else if (secondHasProp){
            // only second has the property
            return 1;
        } else {
            // Neither sort candidate has the passed in property name
            // It is not clear what you want to do here as no other property
            //    name has been specified
            return first[findFirstProperty(first)] - second[findFirstProperty(second)]
        }
    });
}

Working demo: http://jsfiddle.net/jfriend00/PFurT/

Logically, here's what it does:

  1. If both comparison candidates have the desired property, then simply sort by the value of that property.
  2. If only one comparison candidate has the desired property, then make the one with the desired property be first in the sort order
  3. If neither comparison candidate has the desired property, find the first other property on the object and sort by that. This is a guess because you don't really explain what you want to happen in this case, but it works for the data example you provided.

Here's a version that works like the above one, but is has been extended to sort properties that are not the passed in property in alpha order and to deal with empty objects (with no properties) so they go at the end of the sort:

var data = [{c:4},{a:2},{b:2},{a:1},{a:3},{b:3},{b:1},{},{c:3}];

function sortByProperty(array, propName) {
    function findFirstProperty(obj) {
        for (x in obj) {
            if (obj.hasOwnProperty(x)) {
                return x;
            }
        }
    }
    return array.sort(function(first, second) {
        var firstHasProp = propName in first;
        var secondHasProp = propName in second;
        if (firstHasProp) {
            if (secondHasProp) {
                // both have the property
                return first[propName] - second[propName];
            } else {
                // only first has the property
                return -1;
            }
        } else if (secondHasProp){
            // only second has the property
            return 1;
        } else {
            // Neither sort candidate has the passed in property name
            // It is not clear what you want to do here as no other property
            //    name has been specified
            var firstProp = findFirstProperty(first);
            var secondProp = findFirstProperty(second);
            if (firstProp === undefined && secondProp === undefined) {
                return 0;
            } else if (firstProp === undefined) {
                return 1;
            } else if (secondProp === undefined) {
                return -1;
            }
            else if (firstProp === secondProp) {
                return first[firstProp] - second[secondProp];
            } else {
                return firstProp.localeCompare(secondProp);
            }
        }
    });
}

Working demo: http://jsfiddle.net/jfriend00/6QsVv/

The documentation for the sort method is here . The compare function:

should be a function that accepts two arguments x and y and returns a negative value if x < y, zero if x = y, or a positive value if x > y.

The function is passed the values in the array, so it's like calling the function with:

compareFunction({a:2},{b:2});

What you seem to want to do is sort on the property name first, then on the value. The problem with that is that you can't guarantee what order the property names are returned in. In this case, if you have exactly one own property for each object, you can do:

// Return first own property returned by in
// ORDER IS NOT GUARANTEED
function getPropName(o) {
  for (var p in o) {
    if (o.hasOwnProperty(p)) {
      return p;
    }
  }
}

function specialSort(array, key) {
  array.sort(function (a, b) {
    var aProp = getPropName(a);
    var bProp = getPropName(b);
    // If properties are the same, compare value
    if (aProp == bProp) {
      return a[aProp] - b[bProp];
    }

    // Otherwise, compare keys
    return aProp == key? -1 : bProp == key? 1 : aProp.charCodeAt(0) - bProp.charCodeAt(0);
  });
  return array;
}

The above will also sort any other keys (c, d, e, etc.) after the preferred key so:

var a = [{c:3},{a:2},{b:2},{c:2},{a:1},{a:3},{b:3},{b:1},{c:1}]

specialSort(a, 'b'); // [{b:1}, {b:2}, {b:3}, {a:1}, {a:2}, {a:3}, {c:1}, {c:2}, {c:3}]

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