简体   繁体   English

在对象列表上使用.reduce()

[英]using .reduce() on an a list of objects

I have this sample data set: 我有这个样本数据集:

let list = [
  {'first': 'morgan', 'id': 1},
  {'first': 'eric', 'id': 1},
  {'first': 'brian', 'id': 2 },
  {'first': 'derek', 'id' : 2},
  {'first': 'courtney', 'id': 3},
  {'first': 'eric', 'id': 4},
  {'first': 'jon', 'id':4},
]

I am trying to end up with this: 我想结束这个:

[[1, [morgan, eric]], [2, [brian, derek]], [3, [courtney]], [4, [eric, jon]]

I am using the .reduce() function to map over the list. 我使用.reduce()函数映射列表。 However, I'm somewhat stuck. 但是,我有点卡住了。

I got this working: 我有这个工作:

let b = list.reduce((final_list, new_item) => {
  x = final_list.concat([[new_item.id, [new_item.first]]])
  return x
}, [])

However, that flattens out the list into a list of lists of lists, but doesn't combine names that share a similar id. 但是,这会将列表展平为列表列表,但不会合并共享相似ID的名称。

I tried using .map() the code below does not work 我尝试使用.map() ,下面的代码不起作用

I tried to map over the final_list (which is a list of [id, [names]] looking to see if the id of the new_item exists in the smaller_list and then add new_item.first to the smaller_list[1] (which should be the list of names). 我试图映射在final_list(这是[ID,[名]看看是否在的ID列表new_item中存在smaller_list ,然后添加new_item.firstsmaller_list[1]这应该是名单)。

Is this the right approach? 这是正确的方法吗?

let b = list.reduce((final_list, new_item) => {
  final_list.map((smaller_list) => {
    if (smaller_list.indexOf((new_item.id)) >=0) {
      smaller_list[1].concat(new_item.first)
      // not sure what to do here...
    } else {
        // not sure what to do here either...
    }

  })
  x = final_list.concat([[item.id, [item.first]]])
  return x
}, [])

In my approach, i'm using reduce to create a sparse array with the index as the list id. 在我的方法中,我使用reduce来创建一个以索引作为列表id的稀疏数组。 I then use Object.values to squash it down. 然后我使用Object.valuesObject.values它。

 let list = [ {'first': 'morgan', 'id': 1}, {'first': 'eric', 'id': 1}, {'first': 'brian', 'id': 2 }, {'first': 'derek', 'id' : 2}, {'first': 'courtney', 'id': 3}, {'first': 'eric', 'id': 4}, {'first': 'jon', 'id':4} ]; let result = list.reduce( (acc, item, index) => { if(acc[item.id]){ acc[item.id][1].push(item.first); } else { acc[item.id] = [item.id, [item.first]]; } return acc; }, []); console.log(Object.values(result)); 

My solution was to first map over your list and combine the ones with similar ID and then loop over that result in order to change the format to the one requested. 我的解决方案是首先映射您的列表并组合具有相似ID的那些,然后循环该结果,以便将格式更改为所请求的格式。

 let list = [ {'first': 'morgan', 'id': 1}, {'first': 'eric', 'id': 1}, {'first': 'brian', 'id': 2 }, {'first': 'derek', 'id' : 2}, {'first': 'courtney', 'id': 3}, {'first': 'eric', 'id': 4}, {'first': 'jon', 'id':4}, ] let temp = [], result = []; list.map(entry => { if(!temp[entry.id]) temp[entry.id] = [entry.first] else temp[entry.id].push(entry.first) }) temp.forEach((names, id) => { result.push([id, names]) }) console.log(JSON.stringify(result)) 

EDIT: I used an array as temp because the ID is an integer and for the convenience of being able to use a forEach loop. 编辑:我使用数组作为temp因为ID是一个整数 ,为了方便使用forEach循环。 If the key was something else then you would use a Hash table. 如果密钥是其他东西,那么你将使用哈希表。

Try first creating a map of IDs to names, then map that into your final array, eg 首先尝试为名称创建ID映射,然后将其映射到最终数组中,例如

 let list = [ {'first': 'morgan', 'id': 1}, {'first': 'eric', 'id': 1}, {'first': 'brian', 'id': 2 }, {'first': 'derek', 'id' : 2}, {'first': 'courtney', 'id': 3}, {'first': 'eric', 'id': 4}, {'first': 'jon', 'id':4}, ] const idMap = list.reduce((map, item) => { if (Array.isArray(map[item.id])) { map[item.id].push(item.first) } else { map[item.id] = [item.first] } return map }, {}) const b = Object.keys(idMap).map(id => [parseInt(id), idMap[id]]) console.info(JSON.stringify(b)) 

you want to do it with reduce, and doing it with a single reduce would be nice. 你想用reduce做到这一点,用一个reduce做它会很好。 here are 3 ways 这里有3种方式

verbose, more loops 详细,更多循环

const tuples1 = list.reduce( (tuples, obj) => {
  if( tuples.some( ( [id, _] ) => id === obj.id ) ){
    tuples.find( ( [id, _] ) => id === obj.id )[1].push(obj.first)
  }else{
    tuples.push([obj.id, [obj.first]])
  }
  return tuples
}, [])

same as tuples1, terse 和tuples1一样,简洁

const tuples2 = list.reduce( (tuples, obj) =>
  ( tuples.some( ( [id, _] ) => id === obj.id ) ) ?
    ( tuples.find( ( [id, _] ) => id === obj.id )[1].push(obj.first), tuples ) :
    ( tuples.push([obj.id, [obj.first]]), tuples ), [])

more verbose, less loops 更冗长,更少的循环

const tuples3 = list.reduce( (tuples, obj) => {
  const existingId = tuples.find( ( [id, _] ) => id === obj.id )
  if( existingId ){
    existingId[1].push(obj.first)
  }else{
    tuples.push([obj.id, [obj.first]])
  }
  return tuples
}, [])

each produce the same result 每个产生相同的结果

[ [ 1, [ 'morgan', 'eric' ] ],
  [ 2, [ 'brian', 'derek' ] ],
  [ 3, [ 'courtney' ] ],
  [ 4, [ 'eric', 'jon' ] ] ]

Just in case you're also looking for a non reduce solution. 以防你也在寻找非reduce解决方案。

Here is one. 这是一个。 It is a bit long winded but that is because I'm trying to decompose the problem into a few parts. 这有点长,但这是因为我试图将问题分解为几个部分。

  1. I first go through the list and sort the elements into their respective Id . 我首先浏览list并将元素分类到各自的Id

  2. Then For each id in the sorted object. 然后为排序对象中的每个id I loop through it again by first instantiating an outer element first. 我首先通过首先实例化外部元素来再次遍历它。

  3. Pushed the id key into the element first. 首先将id键推入元素中。
  4. Loop through all the associated element and extract their first property and add it into the nested element. 遍历所有关联元素并提取其first属性并将其添加到嵌套元素中。
  5. Once the nesting element is been built. 一旦构建了嵌套元素。 I just merge them up into the parent element and attach it into the result array. 我只是将它们合并到父元素中并将其附加到result数组中。

Hopfully it is not too complicated for you but I suspect it will be easier to see the code in action through an IDE. 对你而言,它并不是太复杂,但我怀疑通过IDE看到代码的运行会更容易。

 var list = [ {'first': 'morgan', 'id': 1}, {'first': 'eric', 'id': 1}, {'first': 'brian', 'id': 2 }, {'first': 'derek', 'id' : 2}, {'first': 'courtney', 'id': 3}, {'first': 'eric', 'id': 4}, {'first': 'jon', 'id':4}, ] var result = []; var keyById = {}; // Sort the list of items to be keyed by: Id => [ item, item ] list.forEach(function(item) { if (!keyById.hasOwnProperty(item.id)) { keyById[item.id] = []; } keyById[item.id].push(item); }); // Build the data structure for (var id in keyById) { var item = []; // The outer element var nestedItem = []; // The nested element belonging to the outer item.push(id); keyById[id].forEach(function(element) { nestedItem.push(element.first); }); item.push(nestedItem); result.push(item); } console.log(JSON.stringify(result, '', 2)); 

Not very sexy, but it works 不是很性感,但它有效

  • Iterate over list and build an object using the id s as key s with value s of array s of the first names. 遍历list和建立使用的对象id S作为key s的valuearray的第first名称。
  • Empty list (if you plan to overwrite it). list (如果您打算覆盖它)。
  • Walk through the resultant object making array s for each id with its next sibling element being an array of the names from the value associated with that id , and push them into list (or a fresh array if maintaining the original list ). 遍历生成的对象,为每个id生成array ,其下一个兄弟元素是与该id相关联的value的名称arraypush它们pushlist (如果保留原始list则将其push送到新array )。

 var list = [ {'first': 'morgan', 'id': 1}, {'first': 'eric', 'id': 1}, {'first': 'brian', 'id': 2 }, {'first': 'derek', 'id' : 2}, {'first': 'courtney', 'id': 3}, {'first': 'eric', 'id': 4}, {'first': 'jon', 'id':4}, ], o = {}, p; list.forEach( ( v ) => { if ( !o[ v.id ] ) { o[ v.id ] = []; } o[ v.id ].push( v.first ); } ); list = []; // if overwriting for ( p in o ) { if ( o.hasOwnProperty( p ) ) { list.push( [ parseInt( p ), o[ p ] ] ); } } console.log( JSON.stringify( list ).replace( /,/g, ", " ) ); 

Edited to add parseInt() (noticed in another answer) since the question's example expected output does indeed show numerals as integers, not strings. 编辑添加parseInt() (在另一个答案中注意到),因为问题的示例预期输出确实将数字显示为整数,而不是字符串。
And changed the demo output to stringify ed JSON (also noticed (easier to read) in other answers). 并将演示输出更改为stringify ed JSON (在其他答案中也注意到(更容易阅读))。

It's probably easiest to write a plain loop for this: 为此编写一个简单的循环可能是最简单的:

let final_list = []
for (k = 0; k < list.length; k += 2) {
    let item1 = list[k]
    let item2 = list[k + 1]
    final_list.push([item1.id, [item1.first, item2.first]])
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM