按重要性顺序对属性排序JSON对象数组

时间:2014-07-30 06:22:57

标签: javascript arrays json sorting

我有一堆歌曲存储在一个JavaScript对象数组中。它看起来像这样:

var library = [
{ "title": "40 Years Later",       "album": "Tears of Steel", "year": 2012, "track": 1, "disk": 1, "time": 31  },
{ "title": "The Dome",             "album": "Tears of Steel", "year": 2012, "track": 2, "disk": 1, "time": 311 },
{ "title": "The Battle",           "album": "Tears of Steel", "year": 2012, "track": 3, "disk": 1, "time": 123 },
{ "title": "End Credits",          "album": "Tears of Steel", "year": 2012, "track": 4, "disk": 1, "time": 103 },
{ "title": "The Wires",           "album": "Elephants Dream", "year": 2006, "track": 1, "disk": 1, "time": 75  },
{ "title": "Typewriter Dance",    "album": "Elephants Dream", "year": 2006, "track": 2, "disk": 1, "time": 70  },
{ "title": "The Safest Place",    "album": "Elephants Dream", "year": 2006, "track": 3, "disk": 1, "time": 45  },
{ "title": "Emo Creates",         "album": "Elephants Dream", "year": 2006, "track": 4, "disk": 1, "time": 60  },
{ "title": "End Title",           "album": "Elephants Dream", "year": 2006, "track": 5, "disk": 1, "time": 91  },
{ "title": "Teaser Music",        "album": "Elephants Dream", "year": 2006, "track": 6, "disk": 1, "time": 75  },
{ "title": "Ambience",            "album": "Elephants Dream", "year": 2006, "track": 7, "disk": 1, "time": 110 },
{ "title": "Snow Fight",                   "album": "Sintel", "year": 2010, "track": 1, "disk": 1, "time": 107 },
{ "title": "Finding Scales / Chicken Run", "album": "Sintel", "year": 2010, "track": 2, "disk": 1, "time": 107 },
{ "title": "The Ziggurat",                 "album": "Sintel", "year": 2010, "track": 3, "disk": 1, "time": 78  },
{ "title": "Expedition",                   "album": "Sintel", "year": 2010, "track": 4, "disk": 1, "time": 93  },
{ "title": "Dragon Blood Tree",            "album": "Sintel", "year": 2010, "track": 5, "disk": 1, "time": 47  },
{ "title": "Cave Fight / Lament",          "album": "Sintel", "year": 2010, "track": 6, "disk": 1, "time": 145 },
{ "title": "I Move On (Sintel's Song)",    "album": "Sintel", "year": 2010, "track": 7, "disk": 1, "time": 169 },
{ "title": "Circling Dragons",             "album": "Sintel", "year": 2010, "track": 8, "disk": 1, "time": 28  },
{ "title": "Trailer Music",                "album": "Sintel", "year": 2010, "track": 9, "disk": 1, "time": 44  }
];

我需要按字母和数字顺序按属性排序。 StackOverflow上有很多关于按单个值排序的文章和问题,有些文章和问题似乎涵盖了多个值,但它们没有顺序或重要性。

我需要按几个值对每个对象(歌曲)进行排序,其中每个值的重要性等级较低。例如:

  

album名称​​(按字母顺序排列)> disk(数字)> track(数字)> title (按字母顺序排列)>等

这意味着相册在一起,每张专辑按字母顺序排列。在每张专辑中都有按磁盘编号排序的歌曲,因此磁盘1中的所有歌曲都在顶部,然后是磁盘2中的所有歌曲等。在每组磁盘编号中都是按曲目编号排序的歌曲。如果存在多首具有相同曲目编号的歌曲,或者不存在曲目编号,则将按字母顺序按歌曲标题对其进行排序。如果曲目标题也相同,则可以给出更多属性。

大写不应该重要,它应该像普通的字母排序那样排序(如果它以数字开头,那将在字母之前,特殊字符就像传统的排序一样)。

我尝试过使用此代码(它具有硬编码的排序值并且没有磁盘编号),但它只是 按代码中最内层属性排序,轨道号。

library.sort(function (a, b) {
    if (a.album === b.album) {
        if (a.track === b.track) {
            var x = a.title.toLowerCase();
            var y = b.title.toLowerCase();
            return x < y ? -1 : x > y ? 1 : 0;
        }
        var x = a.track;
        var y = b.track;
        return x < y ? -1 : x > y ? 1 : 0;
    }
    return a.album.toLowerCase() - b.album.toLowerCase();
});

我正在寻找一个重新排列library数组的函数,以便按照以下输入的方式对其进行排序:

sort(library, [ "album", "disk", "track", "title" ]);

个别值也应该能够按降序排序,可能看起来像-album["album", true]。语法很灵活。

3 个答案:

答案 0 :(得分:3)

您可以使用Alasql JavaScript SQL库对对象数组进行排序。它支持以任何顺序(如SQL)对许多字段进行排序。

对于许多种类,Alasql可能比其他排序函数更快,因为它将查询编译为JavaScript并将这些编译的查询函数保存到缓存中。

var res = alasql('SELECT *, LCASE(album) AS lalbum, LCASE(title) as ltitle FROM ? \
     ORDER BY lalbum DESC, disk, ltrack, ltitle',[library]);

这里我以不区分大小写的方式为排序文本创建了两个附加字段(lalbum和ltitle)。

请与您的数据at jsFiddle

一起试用此示例

答案 1 :(得分:2)

你可以使用这样的函数:

function priority(opt) {
    if (!(opt instanceof Array)) {
        opt = Array.prototype.slice.call(arguments);
    }
    return function (a, b) {
        for (var i = 0; i < opt.length; ++i) {
            var option = opt[i];
            if (typeof option === 'string') {
                option = [option, '+'];
            }
            if (option.length < 2) {
                option[1] = '+';
            }
            if (a[option[0]] !== b[option[0]]) {
                if (a[option[0]] === undefined) return 1;
                if (b[option[0]] === undefined) return -1;
                if (typeof a[option[0]] === 'string' || typeof b[option[0]] === 'string') {
                    return (option[1] === '+' ? String(a[option[0]]).toLowerCase() < String(b[option[0]]).toLowerCase() : String(a[option[0]]).toLowerCase() > String(b[option[0]]).toLowerCase()) ? -1 : 1;
                } else {
                    return (option[1] === '+' ? a[option[0]] < b[option[0]] : a[option[0]] > b[option[0]]) ? -1 : 1;
                }
            }
        }
        return 0;
    };
}

它的工作原理如下:

library.sort(priority(['album', 'disk', ['title', '-']])

这将按照专辑升序排序库,然后按磁盘升序排序,然后按标题降序排序

正式使用:

opt:包含以下内容的数组:

  • string(按键排序&#39;字符串&#39;按升序排列)

  • 阵列
    • 0:string(对键&#39; string&#39;进行排序)
    • 1:&#39; - &#39;或&#39; +&#39;升序或降序(默认为升序)

我没有按照你所说的方式实现它,因为它会使以-开头的键无法按升序排序。

编辑:

备用版本,适用于'-album'语法:

function priority(opt) {
    if (!(opt instanceof Array)) {
        opt = Array.prototype.slice.call(arguments);
    }
    return function (a, b) {
        for (var i = 0; i < opt.length; ++i) {
            var order = opt[i].substr(0, 1),
                key = opt[i].substr(1);
            if (order !== '-' && order !== '+') {
                key = opt[i];
                order = '+';
            }
            if (a[key] !== b[key]) {
                if (a[key] === undefined) return 1;
                if (b[key] === undefined) return -1;
                if (typeof a[key] === 'string' || typeof b[key] === 'string') {
                    return (order === '+' ? String(a[key]).toLowerCase() < String(b[key]).toLowerCase() : String(a[key]).toLowerCase() > String(b[key]).toLowerCase()) ? -1 : 1;
                } else {
                    return (order === '+' ? a[key] < b[key] : a[key] > b[key]) ? -1 : 1;
                }
            }
        }
        return 0;
    };
}

这样使用:

library.sort(priority(['album', '-year', '+title']));
or
library.sort(priority('album', '-year', '+title'));

答案 2 :(得分:1)

在这里,您可以找到解决问题的方法:

// This is kinda hacky, but it works
// "a" means do an alphabetical compare
// "n" means do a numeric compare.
var sortKeys = ["album", "a", "disk", "n", "track", "n", "title", "n"];

function byKey(ao, bo) {
    var l = sortKeys.length;
    var i = 0;
    var sortResult;

    // Walk through the keys  
    while (i < l) {
        // Get the field name
        var field = sortKeys[i];
        // Get the compare type
        var sortType = sortKeys[i + 1];

        // Get the values and force to string values
        var a = "" + ao[field];
        var b = "" + bo[field];

        console.log([field, sortType]);
        // Advance by two because we consume two array elements
        i += 2;

        // Our alphabletical compare
        if (sortType === "a") {
            if (a.toLowerCase() < b.toLowerCase()) {
                return -1;
            }

            if (a.toLowerCase() > b.toLowerCase()) {
                return +1;
            }

            if (a.toLowerCase() === b.toLowerCase()) {
                // Ok, these fields match.  Restart the loop
                // So it will try the next sort criteria in.
                continue;
            }

            throw ("Should never actually get here.");
        }

        if (sortType === "n") {
            // Cheap numeric compare
            return +a - +b;
        }


    }

    // A total match across all fields
    return 0;

}

library.sort(byKey);

console.log(JSON.stringify(library, null, 2));