【问题标题】:Sorting array of JSON objects by properties with an order of importance按重要性顺序对 JSON 对象数组进行排序
【发布时间】:2014-07-30 06:22:56
【问题描述】:

我有一堆存储在 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 数字 (数字) > @987654325 @(按字母顺序) > 等等

这意味着专辑放在一起,每个专辑都按字母顺序排列。在每张专辑中都是按磁盘编号排序的歌曲,因此磁盘 1 中的所有歌曲都在顶部,然后是磁盘 2 中的所有歌曲,依此类推。在每个磁盘编号分组中都是按曲目编号排序的歌曲。如果有多首歌曲具有相同的曲目编号,或者没有曲目编号,它们将按歌曲名称按字母顺序排序。如果track title也一样,可以赋予更多的属性。

大写无关紧要,它应该像正常的字母排序那样排序(如果它以数字开头,那将排在字母之前,特殊字符将像传统排序一样占据一席之地)。

我曾尝试使用此代码(它具有硬编码的排序值且缺少磁盘编号),但它按代码中最内层的属性(轨道号)进行排序。

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]。语法灵活。

【问题讨论】:

    标签: javascript arrays json sorting


    【解决方案1】:

    您可以使用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

    【讨论】:

      【解决方案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: 数组包含:

      • 字符串(按升序对键“字符串”进行排序)

      • 数组
        • 0:字符串(排序键“字符串”)
        • 1:“-”或“+”表示升序或降序(默认为升序)

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

      编辑:

      替代版本,使用'-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'));
      

      【讨论】:

      • 忘记了三元表达式中的&lt;
      • 在您的替代语法版本中,+ 是可选的吗?
      • 是的,+ 是可选的。它可以确保可以有以- 开头的键(只需使用'+--keywithdash' 对这样的键进行升序排序,'---keywithdash' 用于降序)
      • 好主意。我从来没想过!
      • 现在将字符串排序为不区分大小写,同时保留数字和其他类型
      【解决方案3】:

      给你..解决问题的一种方法:

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

      【讨论】:

      • 可以把数字转成字符串,允许用同样的方式排序吗?如果有一个以数字开头的专辑,那将导致问题。
      • 它现在可以处理...尽管只要专辑名称在引号中,无论内容如何,​​它都会是一个字符串。但是,我做了一点改动,以便将严格的数字正确地转换为字符串。同样,任何标记为排序为数字的东西都被强制为数字。
      • 我还应该注意,大小写无关紧要。我不确定它是否在您的解决方案中。
      • 我从您的示例代码中得出了同样的结论。如果您看一下,您会发现我在比较中使用toLowerCase()
      • +1 感谢您的努力,但我不得不把它交给 Ferdi265,因为他是第一个,他的代码更简单,不需要字母/数字规范。但至少你的努力得到了 10 分,谢谢!
      猜你喜欢
      • 1970-01-01
      • 2019-05-29
      • 2018-10-11
      • 2013-11-02
      • 1970-01-01
      • 2019-06-15
      • 1970-01-01
      • 2018-02-07
      相关资源
      最近更新 更多