嵌套的 forEach 循环意外行为
Nested forEach loops unexpected behavior
在我的扑克应用程序中,我有一组手牌,每手牌都是随机选择的具有价值和花色的纸牌对象的数组:
[ [ { 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' } ] ]
为了准备评估手,我想 return 一个手对象数组,每个对象都有一个 values
和 suits
数组。所以输出将是:
[
{
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
根本没有改变。我在这里错过了什么?
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
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
利用数组支持的 map 函数,让 JavaScript 为您代劳,而不是编写大量代码:
let rewritten = data.map(hand => {
return {
values: hand.map(card => card.value),
suits: hand.map(card => card.suit),
}
});
(如果将箭头函数中的外部 { return
和 }
全部放在同一行,则不需要,但出于答案的目的,它的可读性大大降低当然)
这会获取您的原始数据,这些数据已经是手上的数组大小(在本例中为三个),然后对每只手应用转换。具体来说,它创建了一个新对象,其中 "values and suits" 的数组已变成 "just values" 的数组,使用 map
,"just suits" 的数组,也使用 map
.
JavaScript 中的数组附带了一些 packing/unpacking/iterating/transforming 的实用函数,值得一读。它们允许您用很少的代码做大事。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array 值得一读,如果您有一段时间或从未阅读过关于数组的 API。它还解释了如何使用 recude
,例如,它可用于提高此代码的效率(请参阅上面 Jamiec 的回答)。
您可以将值直接映射到属性的对象中。
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; }
您只需要在第二个循环之前创建一个具有两个属性(值和花色)的对象,然后将他注入到第二个循环之后的结果中,如下所示:
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);
在我的扑克应用程序中,我有一组手牌,每手牌都是随机选择的具有价值和花色的纸牌对象的数组:
[ [ { 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' } ] ]
为了准备评估手,我想 return 一个手对象数组,每个对象都有一个 values
和 suits
数组。所以输出将是:
[
{
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
根本没有改变。我在这里错过了什么?
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
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
利用数组支持的 map 函数,让 JavaScript 为您代劳,而不是编写大量代码:
let rewritten = data.map(hand => {
return {
values: hand.map(card => card.value),
suits: hand.map(card => card.suit),
}
});
(如果将箭头函数中的外部 { return
和 }
全部放在同一行,则不需要,但出于答案的目的,它的可读性大大降低当然)
这会获取您的原始数据,这些数据已经是手上的数组大小(在本例中为三个),然后对每只手应用转换。具体来说,它创建了一个新对象,其中 "values and suits" 的数组已变成 "just values" 的数组,使用 map
,"just suits" 的数组,也使用 map
.
JavaScript 中的数组附带了一些 packing/unpacking/iterating/transforming 的实用函数,值得一读。它们允许您用很少的代码做大事。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array 值得一读,如果您有一段时间或从未阅读过关于数组的 API。它还解释了如何使用 recude
,例如,它可用于提高此代码的效率(请参阅上面 Jamiec 的回答)。
您可以将值直接映射到属性的对象中。
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; }
您只需要在第二个循环之前创建一个具有两个属性(值和花色)的对象,然后将他注入到第二个循环之后的结果中,如下所示:
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);