【问题标题】:Perform .join on value in array of objects对对象数组中的值执行 .join
【发布时间】:2013-05-12 12:49:09
【问题描述】:

如果我有一个字符串数组,我可以使用.join() 方法获取单个字符串,每个元素用逗号分隔,如下所示:

["Joe", "Kevin", "Peter"].join(", ") // => "Joe, Kevin, Peter"

我有一个对象数组,我想对其中的一个值执行类似的操作;所以从

[
  {name: "Joe", age: 22},
  {name: "Kevin", age: 24},
  {name: "Peter", age: 21}
]

仅对name 属性执行join 方法,以实现与之前相同的输出。

目前我有以下功能:

function joinObj(a, attr){
  var out = [];

  for (var i = 0; i < a.length; i++){
    out.push(a[i][attr]);
  }

  return out.join(", ");
}

这段代码没有任何问题,它可以工作,但突然之间,我已经从一个简单、简洁的代码行变成了一个非常命令式的函数。有没有更简洁、更实用的写法?

【问题讨论】:

  • 以一种语言为例,Pythonic 的做法是" ,".join([i.name for i in a])
  • 在 ES6 中你可以这样做:users.map(x =&gt; x.name).join(', ');.

标签: javascript arrays object


【解决方案1】:

如果您想将对象映射到某物(在本例中为属性)。我认为Array.prototype.map 是您想要进行功能性编码时正在寻找的东西。

console.log([
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ].map(function(elem){
        return elem.name;
    }).join(","));

在现代 JavaScript 中:

console.log([
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ].map(e => e.name).join(","));

(fiddle)

如果您想支持不是ES5 compliant 的旧版浏览器,您可以对其进行填充(上面的 MDN 页面上有一个 polyfill)。另一种选择是使用 underscorejs 的 pluck 方法:

var users = [
      {name: "Joe", age: 22},
      {name: "Kevin", age: 24},
      {name: "Peter", age: 21}
    ];
var result = _.pluck(users,'name').join(",")

【讨论】:

  • 这个。但是,如果您需要支持此复古浏览器,则需要为 IE
  • @Tommi 好评。我添加了关于 polyfill 的解释以及使用下划线时使用 pluck 的建议。
  • @jackweirdy 我很高兴我能帮上忙 :) 因为它的价值,就像 map/reduce/filter 不是“pythonic”,而理解是另一种方式。在 JavaScript 的新规范(Harmony)以及一些浏览器(firefox)中,你已经有了 Python 的理解。你可以做 [x.name for x of users] (spec wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions) 。值得一提的是,就像使用 map/reduce/filter 并不是很“pythonic”,另一种方式可能适用于 JavaScript。尽管coffeescript.org,CoffeeScript 对他们来说很酷。
  • 只是一个更新 - 数组推导已经从 ES6 中删除,也许我们会在 ES7 中得到它们。
  • 在咖啡脚本中:users.map((obj) -&gt; obj.name).join(', ')
【解决方案2】:

你总是可以覆盖对象的toString 方法:

    var arr = [
        {name: "Joe", age: 22, toString: function(){return this.name;}},
        {name: "Kevin", age: 24, toString: function(){return this.name;}},
        {name: "Peter", age: 21, toString: function(){return this.name;}}
    ];
         
    var result = arr.join(", ");
    console.log(result);

【讨论】:

  • 这是一个非常有趣的方法,我没有意识到你可以在 JS 中做到这一点。虽然数据来自 API,所以它可能不适合我的用例,但绝对值得深思。
  • 看起来不太干
  • @BadHorsie 不,它不干燥。但当然,您可以在数组之外定义一个函数,然后使用 for 循环将此函数分配给所有项目的 toString 方法。或者,您可以在处理构造函数时使用这种方法,通过在构造函数的prototype 属性中添加一个toString 方法。然而,这个例子只是为了展示基本概念。
  • 真正有趣的方法!这种方法对大型阵列的性能有影响吗?
【解决方案3】:

我不知道在不使用外部库的情况下是否有更简单的方法,但我个人喜欢 underscore.js 它有大量用于处理数组、集合等的实用程序。

使用下划线,只需一行代码即可轻松完成:

_.pluck(arr, 'name').join(', ')

【讨论】:

  • 我之前曾涉足下划线,但我希望有一种方法可以在原生 JS 中做到这一点 - 将整个库拉入一次使用一种方法有点愚蠢:)
  • arr 自然是你的数组)
  • 很公平!我发现自己现在到处都使用下划线,所以这不是问题。
【解决方案4】:

试试这个

var x= [
  {name: "Joe", age: 22},
  {name: "Kevin", age: 24},
  {name: "Peter", age: 21}
]

function joinObj(a, attr) {
  var out = []; 
  for (var i=0; i<a.length; i++) {  
    out.push(a[i][attr]); 
  } 
 return out.join(", ");
}

var z = joinObj(x,'name');
z > "Joe, Kevin, Peter"
var y = joinObj(x,'age');
y > "22, 24, 21"

【讨论】:

  • 这与问题中的函数相同,只是由于您对 name 属性进行了硬编码,因此通用性较差。 (a[i].name使用你函数的name参数,它相当于a[i]['name']。)
【解决方案5】:

我也遇到过使用reduce 方法,是这样的:

console.log(
    [
        {name: "Joe", age: 22},
        {name: "Kevin", age: 24},
        {name: "Peter", age: 21}
    ]
    .reduce(function (a, b) {
        return (a.name || a) + ", " + b.name}
    )
)

(a.name || a) 是第一个元素被正确处理,但其余部分(a 是字符串,a.name 未定义)不被视为对象。

编辑:我现在将其进一步重构为:

x.reduce(function(a, b) {return a + ["", ", "][+!!a.length] + b.name;}, "");

我认为它更简洁,因为a 始终是一个字符串,b 始终是一个对象(由于在reduce 中使用了optional initialValue parameter

6 个月后编辑:哦,我在想什么。 “清洁器”。我已经激怒了密码之神。

【讨论】:

  • 谢谢 :D reduce 不像 map 那样受到广泛支持(这很奇怪,因为他们是姐妹)所以我仍在使用你的答案:)
  • 在 6 个月后查看我的编辑,我现在决定我不会雇用自己......它很狡猾,但不干净。
  • 我相信这是最好的答案,因为reduce方法只迭代一次O(n),而map+join迭代2次O(2n)。
  • 阅读本文的学生的小挑剔。大 O 丢弃常数。 O(2n) 实际上是 O(n),使两者在大 O 方面“相等”。可读性(几乎)总是比性能更重要,尤其是当它只是一个恒定的因素差异时。
【解决方案6】:

在节点或 ES6+ 上:

users.map(u => u.name).join(', ')

【讨论】:

    【解决方案7】:

    假设对象数组被变量users引用

    如果可以使用 ES6,那么最简单的解决方案是:

    users.map(user => user.name).join(', ');
    

    如果没有,可以使用lodash

     _.map(users, function(user) {
         return user.name;
     }).join(', ');
    

    【讨论】:

      【解决方案8】:

      我知道的一个旧线程,但仍然与遇到此问题的任何人超级相关。

      Array.map 已被建议在这里,这是我一直使用的一种很棒的方法。 还提到了 Array.reduce...

      我个人会为此用例使用 Array.reduce。 为什么?尽管代码不太干净/清晰。这比通过管道将 map 函数传递给连接要高效得多。

      这样做的原因是因为 Array.map 必须遍历每个元素以返回一个新数组,其中包含数组中对象的所有名称。 Array.join 然后循环遍历数组的内容以执行连接。

      您可以通过使用模板文字将代码放在一行中来提高jackweirdys reduce answer的可读性。 “所有现代浏览器也支持”

      // a one line answer to this question using modern JavaScript
      x.reduce((a, b) => `${a.name || a}, ${b.name}`);
      

      【讨论】:

        【解决方案9】:

        如果对象和动态键:"applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}

        Object.keys(myObject).map(function (key, index) {
            return myObject[key]
        }).join(', ')
        

        【讨论】:

          【解决方案10】:

          不确定,但所有这些答案都是有效的,但不是最佳的,因为它们正在执行两次扫描,您可以在一次扫描中执行此操作。即使 O(2n) 被认为是 O(n) 总是更好拥有一个真正的 O(n)。

          const Join = (arr, separator, prop) => {
              let combined = '';
              for (var i = 0; i < arr.length; i++) {
                  combined = `${combined}${arr[i][prop]}`;
                  if (i + 1 < arr.length)
                      combined = `${combined}${separator} `;
              }
              return combined;
          }
          

          这可能看起来像老派,但允许我这样做:

          skuCombined = Join(option.SKUs, ',', 'SkuNum');
          

          【讨论】:

            【解决方案11】:

            您可以转换为数组,以便获取对象名称

            var objs = [
                  {name: "Joe", age: 22},
                  {name: "Kevin", age: 24},
                  {name: "Peter", age: 21}
                ];
            document.body.innerHTML = Object.values(objs).map(function(obj){
               return obj.name;
            });

            【讨论】:

              【解决方案12】:

              最简单的方法:

              const oldArrayOfObjects = [
              {name: "Bob", age:40}, 
              {name: "Andrew", age:25}, 
              {name: "Peter", age:30}];
              const newArrayOfStringNames = oldArrayOfObjects.map((OA) => OA.name);
              const newArrayOfAges = oldArrayOfObjects.map((OA) => OA.age);
              console.log({newArrayOfStringNames, newArrayOfAges})

              【讨论】:

              • 虽然您的答案使用地图功能来帮助解决原始问题,但它并没有解决原始问题,即干净的方法/实用程序。您的代码还依赖于构建连接的数据结构,因此它也不起作用且不可扩展。想象一下像 join(array, objPath)join(objPath)(array) 这样的实用函数。尝试实现这个join函数。
              猜你喜欢
              • 2022-01-14
              • 1970-01-01
              • 2021-08-06
              • 2018-11-23
              • 1970-01-01
              • 2021-10-24
              • 2019-04-26
              • 1970-01-01
              相关资源
              最近更新 更多