将Array.reduce应用于多维数组以生成对象数组

6
在我的扑克应用中,我有一组手牌,每个手牌都是由具有值和花色的随机选择的卡牌对象数组组成的:
[ [ { 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' } ] ]

为了准备好手牌进行评估,我希望使用 Array.reduce 来返回一个手牌对象数组。因此输出将如下所示:
[ 
  { 
    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来实现这个功能,但是它失败了,我不知道为什么。我有两个console.log,在其中输出符合预期,但最终hands与输入相同。

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

4
"我想使用reduce" ... 那么你的代码尝试去做了吗?这不是一个编写代码的服务。这个网站的工作方式是您发布您的代码,并且人们会帮助您修复它。 - charlietfl
1
我尝试了编写代码,但我认为它们并没有帮助,因为任何有效的答案都需要完全重写我的实现。不过这是公正的批评,我会记在心里的。 - Jackson Lenhart
但你也忽略了一个重点,那就是从错误中学习。不如授人以鱼不如授人以渔 - charlietfl
1
为什么必须使用 reduce 来完成这个任务?使用 forEach 似乎更简单。 - Barmar
@Barmar 我尝试使用嵌套的forEach实现这个功能,但是它失败了,我不知道为什么。我有两个console.log,在其中输出与预期相同,但最终hands与输入相同。这是jsfiddle链接:https://jsfiddle.net/fcp7qLcf/ - Jackson Lenhart
1
el是一个局部变量,它在每次forEach循环中被重置为{}。你的代码从未修改过hands - Barmar
4个回答

16

您需要考虑输入数据(DATA)和输出数据(DATA')的形式。

enter image description here

注意:HANDHAND' 之间存在 1:1 的关系,这意味着我们将使用 Array.prototype.map 进行一次转换。另一方面,CARDHAND' 之间存在 N:1 的关系,这意味着我们将使用 Array.prototype.reduce 进行该转换。

enter image description here

在我们工作时,请记住我们将进行映射归约

const 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' } ] ]

let output = 
  data.map(cards =>
    cards.reduce(({values, suits}, {value, suit}) => ({
      values: [...values, value],
      suits: [...suits, suit]
    }), {values: [], suits: []}))

console.log(output)

现在当然看起来有点密集,如果我们能降低一些复杂性就好了。通过为map和reduce制作一些柯里化适配器,我们可以很好地表达执行转换的函数。

const 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' } ] ]


const map = f => xs => xs.map(f)

const reduce = f => y => xs => xs.reduce(f, y)

const handAppendCard = ({values, suits}, {value, suit}) => ({
  values: [...values, value],
  suits: [...suits, suit]
})

const makeHands =
  map (reduce (handAppendCard) ({values:[], suits:[]}))

let output = makeHands (data)

console.log(output)

这只是一种解决问题的方法。希望你能从中学到一些东西 ^_^


2

这里有一个使用嵌套的Array.prototype.reduce函数的解决方案:

var array=[[{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 result = array.reduce(function(p, c) {
      p.push(c.reduce(function(a, b) {
        a.values.push(b.value);
        a.suits.push(b.suit);
        return a;
      }, {values: [],suits: []}));
    return p;
  },[]);

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


0

您可以使用reduce flat来提取嵌套数组。如果传递Infinity,无论它们有多深,都将被提取。

const result = flatten([1,2,[3,4],[5,6,7]]) // result: [1,2,3,4,5,6,7]

所有东西都变成一维数组


-1
这是一个简单的解决方案,使用字符串拼接和reduce。你可以尝试类似以下的代码:
var reduced = []; 
//here a is your initial array
for(var i=0; i<a.length;i++){
    reduced.push(a[i].reduce(function(prev,curr){
        var obj={value:prev.value+','+curr.value,suit:prev.suit+','+curr.suit};return obj}));
}
console.log(reduced)

编辑: 根据@Barmar评论,此返回字符串。如果您想要一个数组,您可以做到:

for(var i=0; i<a.length;i++){
    var tempElm =a[i].reduce(function(prev,curr) {
        var obj= {value:prev.value+','+curr.value,suit:prev.suit+','+curr.suit};return obj}); 
        tempElm['value'] = tempElm['value'].split();
        tempElm['suit']= tempElm['suit'].split();
        reduced.push(tempElm);
}
console.log(reduced)

编辑2: 针对上述修复的公正批评(增加了将字符串转换为数组的开销),您可以直接创建数组,如下所示:

var reduced = []; 
for(var i=0; i<a.length;i++){ 
    var valArray = []; var suitArray=[];
    var tempElm = a[i].reduce(function(prev,curr) {
        valArray.push(curr.value);suitArray.push(curr.suit);
        var obj= {value:valArray,suit:suitArray};
        return obj;
    },null); 
console.log(reduced)

2
他想要的是数组作为结果,而不是连接后的字符串。 - Barmar
@Barmar 谢谢您的评论...已编辑帖子以反映更改。 - Nishanth Matha
1
这是一个愚蠢的修复方法。为什么要创建一个字符串,然后再将其拆分成数组呢?你可以直接在开始时创建一个数组不是吗? - Barmar
@Barmar 这是一个公正的评论。请查看编辑2,然后展示你所拥有的 :D ... - Nishanth Matha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接