嵌套的forEach循环意外行为

时间:2016-12-20 17:06:48

标签: javascript arrays foreach poker

在我的扑克应用程序中,我有一排手,每一手都是随机选择的卡片对象,有价值和适合:

[ [ { value: 5, suit: 's' },
    { value: 4, suit: 's' },
    { value: 6, suit: 'c' },
    { value: 11, suit: 'd' },
    { value: 12, suit: 'c' } ],
  [ { value: 9, suit: 'd' },
    { value: 12, suit: 'h' },
    { value: 8, suit: 'c' },
    { value: 12, suit: 's' },
    { value: 2, suit: 's' } ],
  [ { value: 4, suit: 'h' },
    { value: 6, suit: 's' },
    { value: 10, suit: 'c' },
    { value: 3, suit: 'd' },
    { value: 7, suit: 'd' } ] ]

为了准备评估的手,我想返回一个手对象数组,每个对象都有一个valuessuits数组。所以输出将是:

[ 
  { 
    values: [5, 4, 6, 11, 12],
    suits: ['s', 's', 'c', 'd', 'c'] 
  },      
  { 
    values: [9, 12, 8, 12, 2], 
    suits: ['d', 'h', 'c', 's', 's'] 
  },
  { 
    values: [4, 6, 10, 3, 7],
    suits: ['h', 's', 'c', 'd', 'd'] 
  } 
]

我正在尝试使用嵌套的forEach循环来实现这一点,如下所示:

let temp = []
hands.forEach((el) => {
  temp = el
  el = {}
  el.values = []
  el.suits = []
  temp.forEach((obj) => {
    el.values.push(obj.value)
    el.suits.push(obj.suit)
    console.log(el) //expected output
  })
  console.log(el) //expected output
})
console.log(hands) //same as original

然而,正如评论所概述的那样,它在循环结束之前表现得如预期,hands根本没有改变。我在这里缺少什么?

5 个答案:

答案 0 :(得分:4)

forEach不会更改调用它的数组,它只是迭代调用每个元素的函数的数组。如果你想在该函数中构建一个新数组,那就这样做

var newArray = [];
hands.forEach((el) => {
   var newValue = // do something here
   newArray.push(newValue);
});

我认为你要做的是:



var hands = [ [ { value: 5, suit: 's' },
    { value: 4, suit: 's' },
    { value: 6, suit: 'c' },
    { value: 11, suit: 'd' },
    { value: 12, suit: 'c' } ],
  [ { value: 9, suit: 'd' },
    { value: 12, suit: 'h' },
    { value: 8, suit: 'c' },
    { value: 12, suit: 's' },
    { value: 2, suit: 's' } ],
  [ { value: 4, suit: 'h' },
    { value: 6, suit: 's' },
    { value: 10, suit: 'c' },
    { value: 3, suit: 'd' },
    { value: 7, suit: 'd' } ] ];

let newValues = []
hands.forEach((el) => {
  var temp = {values:[], suits:[]};
  el.forEach((obj) => {
    temp.values.push(obj.value)
    temp.suits.push(obj.suit)
    
  })
  newValues.push(temp);
})
console.log(newValues) // NOT same as original




有很多方法可以实现同样的目标,我能想出的最好的方法是 - 并避免在其他答案中看到的双hand.map(效率非常低)。



var hands = [ [ { value: 5, suit: 's' },
    { value: 4, suit: 's' },
    { value: 6, suit: 'c' },
    { value: 11, suit: 'd' },
    { value: 12, suit: 'c' } ],
  [ { value: 9, suit: 'd' },
    { value: 12, suit: 'h' },
    { value: 8, suit: 'c' },
    { value: 12, suit: 's' },
    { value: 2, suit: 's' } ],
  [ { value: 4, suit: 'h' },
    { value: 6, suit: 's' },
    { value: 10, suit: 'c' },
    { value: 3, suit: 'd' },
    { value: 7, suit: 'd' } ] ];

let newValues = hands.map(h => {
      return h.reduce( (p,c) => {
            p.values.push(c.value);
            p.suits.push(c.suit);
            return p;
        },{values:[], suits:[]});
  });

console.log(newValues) // NOT same as original




答案 1 :(得分:1)

hands.forEach((el) => {

依次将数组中的每个对象分配给变量el

el = {}

您使用对新对象的引用覆盖变量 el

el不再与其原始值相关联。

只是替换el的值。数组中的属性仍然指向原始对象。

要更改原始数组中的对象,您需要明确地为它们分配一个新对象。

hands.forEach((originalValue, index, array) => {
  // originalValue is what you used to call `temp`
  // Just make `el` a new object
  var el = {};
  // Put it in the array
  array[index] = el;
  // Then continue as before

答案 2 :(得分:1)

让JavaScript为你做这件事而不是编写大量代码,利用数组支持的map函数:

let rewritten = data.map(hand => {
  return {
    values: hand.map(card => card.value),
    suits: hand.map(card => card.suit),
  } 
});

(箭头函数中的外部{ return}是不必要的,如果你把它全部放在同一行上,但是对于答案来说,当然它的可读性要低得多)

这将获取您的原始数据,这些数据已经在手上(在这种情况下为三个),然后对每只手应用变换。具体来说,它创建了一个新的对象,其中"值的数组和套装"已经使用map转换为"只是值",以及#34;只是适合",也使用map

JavaScript中的数组带有许多实用程序函数,用于打包/解包/迭代/转换,值得给予读者通过。它们允许你用很少的代码做大事。

如果您在一段时间内或之前没有读过数组API,那么

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array值得一读。它还解释了如何使用recude,这可以用来提高代码的效率(请参阅Jamiec上面的答案)。

答案 3 :(得分:1)

您可以直接在属性的对象中映射值。

var data = [[{ value: 5, suit: 's' }, { value: 4, suit: 's' }, { value: 6, suit: 'c' }, { value: 11, suit: 'd' }, { value: 12, suit: 'c' }], [{ value: 9, suit: 'd' }, { value: 12, suit: 'h' }, { value: 8, suit: 'c' }, { value: 12, suit: 's' }, { value: 2, suit: 's' }], [{ value: 4, suit: 'h' }, { value: 6, suit: 's' }, { value: 10, suit: 'c' }, { value: 3, suit: 'd' }, { value: 7, suit: 'd' }]],

result = data.map(a => (
  { 
    values: a.map(b => b.value), 
    suits: a.map(b => b.suit) 
  })
);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

您可以使用较少的迭代样式。

var data = [[{ value: 5, suit: 's' }, { value: 4, suit: 's' }, { value: 6, suit: 'c' }, { value: 11, suit: 'd' }, { value: 12, suit: 'c' }], [{ value: 9, suit: 'd' }, { value: 12, suit: 'h' }, { value: 8, suit: 'c' }, { value: 12, suit: 's' }, { value: 2, suit: 's' }], [{ value: 4, suit: 'h' }, { value: 6, suit: 's' }, { value: 10, suit: 'c' }, { value: 3, suit: 'd' }, { value: 7, suit: 'd' }]],
    keys = ['value', 'suit'],
    result = data.map(
        a => a.reduce(
            (r, b) => (keys.forEach(k => r[k + 's'].push(b[k])), r), { values: [], suits: [] }
        )
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 4 :(得分:1)

你只需要在第二个循环之前创建一个具有两个属性(值和套装)的对象,并在第二个循环之后将其注入结果:



var hands = [[{ value: 5, suit: 's' }, { value: 4, suit: 's' }, { value: 6, suit: 'c' }, { value: 11, suit: 'd' }, { value: 12, suit: 'c' }], [{ value: 9, suit: 'd' }, { value: 12, suit: 'h' }, { value: 8, suit: 'c' }, { value: 12, suit: 's' }, { value: 2, suit: 's' }], [{ value: 4, suit: 'h' }, { value: 6, suit: 's' }, { value: 10, suit: 'c' }, { value: 3, suit: 'd' }, { value: 7, suit: 'd' }]];
var output = [];
hands.forEach(x => {
  var temp = {values: [], suits: []};
  x.forEach(y => {
    temp.values.push(y.value);
    temp.suits.push(y.suit);
  })
  output.push(temp);
});
console.log(output);