简体   繁体   English

在ES6中将对象映射到对象数组

[英]Mapping an object to array of objects in ES6

How would I convert a object to an array of objects while keeping key names? 如何在保留键名的同时将对象转换为对象数组?

// actual 
obj = {
  key1: null,
  key2: "Nelly",
  key3: [ "suit", "sweat" ]
} 

// expected 
arr = [
  { key2: "Nelly" },
  { key3: [ "suit", "sweat" ] }
]

currently my solution is... 目前我的解决方案是...

 var arr = Object.keys(obj).map(key => { if (obj[key]) return { key: obj[key] } });

which returns 哪个返回

arr = [
  undefined,
  { key: "Nelly" },
  { key: [ "suit", "sweat" ] }
]

Transducers 换能器

There's heaps of answers here to help you reach your answer in a practical way – filter this, map that, and voilà, the result you're looking for. 这里有很多答案,可以帮助您以实用的方式获得答案-对此进行filtermap并确定所需的结果。 There's other answers using primitive for loops, but those make you sad. 还有使用原始的其他答案for循环,但那些让你伤心。

So you're wondering, "is it possible to filter and map without iterating through the array more than once?" 因此,您想知道“是否可以不重复遍历数组就进行过滤映射?” Yes, using transducers . 是的,使用换能器


Runnable demo 可运行的演示

I might update this paragraph with more code explanation if necessary. 如有必要,我可能会在本段中添加更多代码说明。 ES6 comin' at you … ES6来了……

 // Trans monoid const Trans = f => ({ runTrans: f, concat: ({runTrans: g}) => Trans(k => f(g(k))) }) Trans.empty = () => Trans(k => k) const transduce = (t, m, i) => i.reduce(t.runTrans((acc, x) => acc.concat(x)), m.empty()) // complete Array monoid implementation Array.empty = () => [] // transducers const mapper = f => Trans(k => (acc, x) => k(acc, f(x))) const filterer = f => Trans(k => (acc, x) => f(x) ? k(acc, x) : acc) const logger = label => Trans(k => (acc, x) => (console.log(label, x), k(acc, x))) // your function, implemented with transducers const foo = o => { const t = logger('filtering') .concat(filterer(k => o[k] !== null)) .concat(logger('mapping')) .concat(mapper(k => ({ [k]: o[k] }))) .concat(logger('result')) return transduce(t, Array, Object.keys(o)) } console.log(foo({a: null, b: 2, c: 3})) 

Output; 输出; notice the steps appear interlaced – filtering , mapping , result , repeat – this means each of the combined transducers run for each iteration of the input array. 注意,步骤以隔行方式出现- 过滤映射结果 ,重复-这意味着每个组合换能器针对输入阵列的每次迭代运行。 Also notice how because a 's value is null , there is no mapping or result step for a ; 还要注意如何因为a的值是null ,不存在用于映射结果步骤a ; it skips right to filtering b – all of this means we only stepped thru the array once . 它跳过了过滤 b权利-所有这些意味着我们只通过数组一次

// filtering a
// filtering b
// mapping b
// result { b: 2 }
// filtering c
// mapping c
// result { c: 3 }
// => [ { b: 2 }, { c: 3 } ]

Finishing up 整理起来

Of course that foo function has lots of console.log stuff tho. 当然,该foo函数具有很多console.log内容。 In case it's not obvious, we just want to remove the logger transducers for our actual implementation 如果情况不太明显,我们只想删除logger传感器即可进行实际实施

const foo = o => {
  const t = filterer(k => o[k] !== null)
    .concat(mapper(k => ({ [k]: o[k] })))
  return transduce(t, Array, Object.keys(o))
}

console.log(foo({a: null, b: 2, c: 3}))
// => [ {b: 2}, {c: 3} ]

Attribution 归因

My enlightenment on the subject is owed exclusively to Brian Lonsdorf and accompanying work: Monoidal Contravariant Functors Are Actually Useful 我对这个问题的启示完全归功于Brian Lonsdorf及其伴随的工作:单等逆函子实际上是有用的

.map() returns an array of the same length as the original array. .map()返回长度与原始数组相同的数组。 Code like yours with a callback that doesn't return a value in some cases will result in elements with the value undefined . 像您这样的带有回调的代码在某些情况下不会返回值,将导致元素的值undefined One way to deal with that is to first .filter() out the elements you don't want to keep. 一种解决方法是首先.filter()删除您不想保留的元素。

Anyway, to get the key names you want you can use an object literal with a computed property name : 无论如何,要获取所需的键名,可以使用带有计算出的属性名的对象文字:

{ [key]: obj[key] }

In context: 在上下文中:

 const obj = { key1: null, key2: 'Nelly', key3: [ 'suit', 'sweat' ] } const arr = Object.keys(obj) .filter(v => obj[v] != null) .map(key => ({ [key]: obj[key] })) console.log(arr) 

As @zerkms says, I don't think using multiple es6 functions is going to improve your code. 正如@zerkms所说,我认为使用多个es6函数不会改善您的代码。 Try a loop! 尝试循环!

// actual 
let obj = {
  key1: null,
  key2: "Nelly",
  key3: [ "suit", "sweat" ]
};

let arr = [];
let k = Object.keys(obj);

for(let i = 0, len = k.length; i < len; i++) {
  let key = k[i];
  if (obj[key]) {
    arr.push({key: obj[key]});
  }
}

If you use map , the length of your expected array will be the same as the number of keys in your input. 如果使用map ,则期望数组的长度将与输入中的键数相同。 So map is not appropriate in this case. 因此,在这种情况下, map是不合适的。 My solution is to use a reduce function like so: 我的解决方案是使用像这样的reduce函数:

 var obj = { key1: null, key2: 'Nelly', key3: [ 'suit', 'sweat' ] } var res = Object.keys(obj).reduce( (acc, curr) => { // if current key's value is not null // insert object to the resulting array acc if (obj[curr]) { acc.push({[curr] : obj[curr]}); return acc; } // if they key value is null, skip it return acc; }, [] ); console.log(res); 

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

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