【问题标题】:How to sort a Javascript object, or convert it to an array?如何对 Javascript 对象进行排序,或将其转换为数组?
【发布时间】:2011-06-23 05:30:20
【问题描述】:

我有一些从服务器获取的 JSON 数据。在我的 JavaScript 中,我想对其进行一些排序。我认为 sort() 函数会做我想做的事。

但是,JavaScript 似乎在 JSON 数据到达时立即将其转换为 Object。如果我尝试使用 sort() 方法,我会收到很多错误(使用 Firebug 进行测试)。

我在网上看了一圈,似乎每个人都在说,一方面,JSON 对象已经是 JavaScript 数组,而且对象可以像数组一样对待。就像在this question 上一样,在其中一个答案中,有人说“[Object object] 是您的数据——您可以像访问数组一样访问它。”

然而,这并不完全正确。 JavaScript 不允许我在对象上使用 sort()。而且由于默认假设是它们都是相同的东西,因此似乎没有任何关于如何将 Object 转换为 Array 或强制 JavaScript 将其视为一个或类似的说明。

那么...如何让 JavaScript 让我将这些数据视为一个数组并对其进行排序()?

我的对象的控制台日志输出如下所示(我希望能够按“级别”中的值进行排序):

对象 JSON 数据

{ 
1: {
    displayName: "Dude1",
    email: "dude1@example.com<mailto:dude1@example.com>",
    lastActive: 1296980700, 
    level: 57, 
    timeout: 12969932837
}, 2: {
    displayName: "Dude2",
    email: "dude2@example.com<mailto:dude2@example.com>",
    lastActive: 1296983456,
    level: 28,
    timeout: 12969937382
}, 3: {
    displayName: "Dude3",
    email: "dude3@example.com<mailto:dude3@example.com>",
    lastActive: 1296980749,
    level: 99,
    timeout: 129699323459
} 
}

【问题讨论】:

  • 如果你想要Array,为什么不使用 JSON 数组呢? ["foo","bar","baz"]Array{0:"foo",1:"bar",2:"baz"}Object... 并不是所有带有数字键的 Objects 都是 Arrays,因为其中一个 Array 有一个 length 和一堆方法定义 Object

标签: javascript arrays json sorting object


【解决方案1】:

Array.prototype.slice.call(arrayLikeObject)

是将类数组对象转换为数组的标准方法。

这只适用于arguments 对象。将泛型对象转换为数组有点痛苦。以下是underscore.js的来源:

_.toArray = function(iterable) {
    if (!iterable)                return [];
    if (iterable.toArray)         return iterable.toArray();
    if (_.isArray(iterable))      return iterable;
    if (_.isArguments(iterable))  return slice.call(iterable);
    return _.values(iterable);
};

_.values = function(obj) {
    return _.map(obj, _.identity);
};

事实证明,您需要自己循环对象并将其映射到数组。

var newArray = []
for (var key in object) {
    newArray.push(key);
}

您混淆了数组和“关联数组”的概念。在 JavaScript 中,对象有点像关联数组,因为您可以访问 object["key"] 格式的数据。它们不是真正的关联数组,因为对象是无序列表。

对象和数组有很大的不同。

下划线使用示例:

var sortedObject = _.sortBy(object, function(val, key, object) {
    // return an number to index it by. then it is sorted from smallest to largest number
    return val;
});

live example

【讨论】:

  • 我可以在 Javascript 中对关联数组进行排序吗?假设键中的值是可排序的(键名是文本,但值是数字。)
  • @user184108 不。没有本地排序。看看 underscore.js,它实现了 _.sortBy 之类的功能,要么使用下划线,要么编写一个小库来为你做这件事。
  • 呃……这整个事情都超出了我的菜鸟头。我已经下载了下划线代码...所以我可以使用下划线将我的对象转换为数组,然后对该数组进行排序...?
  • @user184108 可以看到编辑。使用 jsfiddle 并查看 underscore.js 文档。
  • _.sortyBy 函数成功了!效果很好。用它将我的对象复制到一个新数组中,之后,数组 sort() 函数像宣传的那样工作。它似乎不需要指定“key”和“object”参数,但如果我将它们完全取出会破坏代码。所以我按照示例将它们留在了里面。谢谢大家的帮助!!
【解决方案2】:

你应该能够像这样将 JavaScript 对象转换为数组...

var obj = {
    '1': 'a',
    '2': 'b',
    '3': 'c'  
};

var arr = [];

for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      arr.push(obj[key]);  
    }
}

console.log(arr); // ["a", "b", "c"]

See it on jsFiddle.

【讨论】:

  • 好的,这样做了,但是如果我将数组的结果回显到控制台中,它似乎是空的。这只是两个方括号。 [ ]
  • @user184108 您可以为您的对象发布console.log() 的输出吗?
  • @alex 尝试传入一个对象 {"1": 1}。 Array.prototype.slice.call 仅适用于 arguments 对象
  • @Raynos 我明白了。我猜下一件事是显式循环并一次添加一个?
  • @alex 这就是 underscore.js 我怀疑有更好的方法。
【解决方案3】:

如果你的 JavaScript 对象是一个类数组对象,即具有有效数字length属性的Object实例,那么你可以直接使用很多native由于call 方法,它上面的数组方法。例如:

// Sorts the given objet in-place as if it was an array
Array.prototype.sort.call(yourObject);

因此,如果您知道要排序的条目数 (How to efficiently count the number of keys/properties of an object in JavaScript?),您可以这样做:

yourObject.length = theNumberOfEntries;
Array.prototype.sort.call(yourObject);
// Optionally: delete yourObject.length;

请注意,这只会将按“0”、“1”、“2”、... 索引的属性排序到 length - 1(含),就像在数组中一样。对象的其他属性不会重新排序。

【讨论】:

  • 非常有趣,但我不明白您所说的索引属性是什么意思。你能举个例子吗?谢谢
  • @charlysisto 我在这里所说的“属性索引”只是它的键,用于访问它的值。当上下文表明我们正在处理数字键时,如果我知道它们是文本的,我喜欢称它们为“属性索引”和“属性名称”。在 'var obj = { "length": 3, "1": value1, "2": value2, "3": value3 }` 我们有四个属性,其索引分别为 "length" "1" "2" 和 " 3"。
【解决方案4】:

我最近在尝试按其中一个属性对一组对象进行分组时偶然发现了这个问题,导致我无法对一个对象进行排序。

具体来说,这是我想按年份分组并按年份降序排列的一系列博客文章。我使用了下划线的实用程序:

var grouped = _.groupBy(blogposts, function(post){
  var date = new Date(post.publication_date)
  return date.getFullYear()
})
//=> { 2010: [blogpost, blogpost,etc], 2011: [blogpost, etc] }

正如@Raynos 解释的那样,我必须先得到某种数组,然后再对其进行排序......

原来 underscore (1.4) 有一个不错的小实用程序,叫做 pairs,它会将对象的 {key: value} 映射到 [key, value] 数组中。相当于:

var paired = _.map(grouped, function(val, key){
  return [key, val]
})
//=> [ [2010, [blogpost, blogpost, ...] ], [2011, [blogpost, blogpost, ...]]...]

从那里您可以轻松地按每对的第一项进行排序。

这是最终结果:

var grouped = _.groupBy(result.resource, function(resource){
  var date = new Date(resource.pub_date)
  return date.getFullYear() //+ "." + (date.getMonth()+1)
})

var paired = _.pairs(grouped)

var sorted = _.sortBy(paired, function(pairs){
  return -parseInt(pairs[0])
})

return sorted;
// Giving me the expected result:
//=> [ [2013, [blogpost, blogpost, ...] ], [2012, [blogpost, blogpost, ...]]...]

我确信虽然有更好、更高效的方法,但是来自 ruby​​ 的这段代码对我来说是立即可以理解的。

【讨论】:

    【解决方案5】:

    jQuery 提供了一个 map 函数,它将遍历数组或对象中的每个元素,并将结果映射到一个新数组中。

    在 jQuery 1.6 之前,$.map() 仅支持遍历数组。

    我们可以使用它来将任何对象转换为数组,如下所示...

      myArray = $.map(myObject, function (el) {
        return el;
      });
    

    但是…… 如果回调函数返回 null 或 undefined,则该值将从数组中删除,在大多数情况下这很有用,但如果您需要 null 值,则可能会导致问题我的数组。

    jQuery 提供了一个解决方案... 将值作为具有单个值的数组返回

    myArrayWithNulls = jQuery.map(myObject, function (el) {
      return [el];
    });
    

    这是一个演示这两种方法的小提琴: http://jsfiddle.net/chim/nFyPE/

    http://jsperf.com/object-to-array-jquery-2

    【讨论】:

      【解决方案6】:

      我编写了一个小函数来递归地将具有属性的对象转换为多维数组。此代码依赖于 forEach 和 toArray 方法的下划线或 lodash。

      function deepToArray(obj) {
          var copy = [];
      
      
          var i = 0;
          if (obj.constructor == Object ||
              obj.constructor == Array) {
      
              _.forEach(obj, process);
      
          } else {
      
              copy = obj;
      
          }
      
      
          function process(current, index, collection) {
      
              var processed = null;
              if (current.constructor != Object &&
                  current.constructor != Array) {
                  processed = current;
              } else {
                  processed = deepToArray(_.toArray(current));
              }
      
              copy.push(processed);
      
          }
      
          return copy;
      }
      

      这是小提琴: http://jsfiddle.net/gGT2D/

      注意:这是为了将原本是数组的对象转换回数组,因此任何非数组索引键值都将丢失。

      【讨论】:

        【解决方案7】:

        这些答案中的大多数使问题过于复杂或使用 JQuery 或 Underscore,而 OP 从未要求过这些。

        您可以像这样将对象转换为数组:

        myArray= Object.keys(data).map(function(key) { return data[key] });
        

        并像这样对结果进行排序:

        myArray.sort(function(x, y) {return x.level - y.level});
        

        如果你需要 id/index,那么你需要做更多:

        Object.keys(data).map(function(key) { 
          var obj = data[key];
          obj.index = key;
          return obj 
        });
        

        【讨论】:

        • 我假设map返回的数组的顺序是确定的?
        • map 将始终以与提供的数组相同的顺序产生,因此它是确定性的,但在这种情况下,它来自不保证任何顺序的 Object.keys(),所以总的来说这个解决方案不是确定性的,其他答案都不是,除非您添加自己的排序。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-08-16
        • 2019-10-01
        • 1970-01-01
        • 2016-09-13
        • 1970-01-01
        • 2010-11-28
        • 1970-01-01
        相关资源
        最近更新 更多