为具有相同键的每个项目创建一个对象属性数组

问题描述 投票:0回答:7

我将两个对象合并在一起以创建一个过滤器对象。但是我想对键相同的合并对象属性值进行分组。

所以...

[{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}]

成为

[{category: ['furniture', 'mirrors']}, {availability: 'in_stock'}]

有什么想法吗?

javascript arrays object ecmascript-6 lodash
7个回答
3
投票

使用 lodash,您可以通过扩散到

_.mergeWith()
将整个数组合并到一个新对象。定制程序应使用空数组作为当前值的默认值,并连接这些值。使用
_.map()
转换回数组。

const data = [{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}];

const result = _.map(
  _.mergeWith({}, ...data, (a = [], b = [], key) => a.concat(b)),
  (val, key) => ({ [key]: val })
)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

使用vanilla JS,将数组简化为Map,使用对象的键作为Map的键,使用空数组作为值,并将对象的值推入数组。使用

Array.from()
将 Map 转换为数组。

const data = [{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}];

const result = Array.from(
  data.reduce((acc, obj) => {
    Object.entries(obj)
      .forEach(([key, val]) => {
        if(!acc.has(key)) acc.set(key, [])

        acc.get(key).push(val)
      })

    return acc
  }, new Map()),
  ([key, val]) => ({ [key]: val })
)

console.log(result)


2
投票

您可以像这样使用

reduce

const data = [
  { category: 'furniture' }, 
  { category: 'mirrors' }, 
  { availability: 'in_stock' }
];

const result = data.reduce(
  (a, x) => {
    const key = Object.keys(x)[0]; // find the key of the current object
    if (!a.tmp[key]) { // if the current key doesn't exist in the lookup object (tmp) yet ...
      a.tmp[key] = []; // create an empty array in the lookup object for the current key
      a.result.push({ [key]: a.tmp[key] }); // push the current object to the result
    }
    a.tmp[key].push(x[key]); // push the current value to the array
    return a;
  }, 
  { result: [], tmp: {} },
).result;

console.log(result);

我确信有更简单的方法可以实现这一目标,但这是我现在能想到的最好的方法。


1
投票

我们也可以通过使用

forEach
循环来实现这一点 :

const input = [{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}];

const resultObj = {};
const resultArr = [];

input.forEach((obj) => {
  resultObj[Object.keys(obj)[0]] = [];
})

input.forEach((obj) => {
  resultObj[Object.keys(obj)[0]].push(obj[Object.keys(obj)[0]]);
  resultArr.push(resultObj);
})

console.log([...new Set(resultArr)]);


1
投票

另一种减少解决方案

const arr = [{category: 'furniture', category2: 'furniture2'}, {category: 'mirrors'}, {availability: 'in_stock'}]


const result = Object.values(arr
  .flatMap((obj) => Object.entries(obj))
  .reduce((acc, [key, value]) => {
    acc[key] = acc[key] 
      ? {[key]: [...acc[key][key], value] } 
      : {[key]: [value] }
  
  return acc;
  }, {}));
  
console.log(result)
.as-console-wrapper{min-height: 100%!important; top: 0}


1
投票

通用实现可以实现任何类型对象的合并,无论对象属性名称的数量和类型如何。

由于这种实现的结果是一个对象,因此需要进行额外的处理才能满足OP的要求。

function mergeAndCollectItemEntries(result, item) {
  // return the programmatically aggregated merger/result.
  return Object
    // get an item's entry array.
    .entries(item)
    // for each key-value pair ...
    .reduce((merger, [key, value]) => {

      // ... access and/or create a `key` specific array ...
      // ... and push `value` into this array.
      (merger[key] ??= []).push(value);

      // return the programmatically aggregated merger/result.
      return merger;

    }, result);
}

const sampleData = [
  { category: 'furniture' },
  { category: 'mirrors' },
  { availability: 'in_stock' },
];
const mergedData = sampleData
  .reduce(mergeAndCollectItemEntries, {});

const mergedDataList = Object
  .entries(
    sampleData
      .reduce(mergeAndCollectItemEntries, {})
  ) 
  .map(entry => Object.fromEntries([entry]));
//.map(([key, value]) => ({ [key]: value }));

console.log({
  sampleData,
  mergedData,
  mergedDataList,
});
console.log(
  Object
    .entries([
        { category: 'furniture', foo: 'baz' },
        { category: 'mirrors', bar: 'bizz' },
        { availability: 'in_stock', bar: 'buzz' },
      ].reduce(
        mergeAndCollectItemEntries, {}
      )
    ).map(
      ([key, value]) => ({ [key]: value })
    //entry => Object.fromEntries([entry])
    )
);
.as-console-wrapper { min-height: 100%!important; top: 0; }


0
投票

这里的另一种方法是构建跟踪对象来合并值。

根据预期输出处理单个值保留为字符串和多个值保留为数组的情况。

const merge = (arr, output = {}) => {
  arr.forEach((item) => {
    const [[key, val]] = Object.entries(item);
    if (key in output) {
      output[key] = Array.isArray(output[key])
        ? output[key].concat(val)
        : [output[key]].concat(val);
    } else {
      output[key] = val;
    }
  });
  return Object.entries(output).map(([key, val]) => ({ [key]: val }));
};

const data = [
  { category: "furniture" },
  { category: "mirrors" },
  { availability: "in_stock" },
];

console.log(merge(data));


0
投票

基于

Object.groupBy
构建的解决方案:

const arr = [{category: 'furniture'}, {category: 'mirrors'}, {availability: 'in_stock'}];
const result = Object.fromEntries(Object.entries(
    Object.groupBy(arr.flatMap(Object.entries), ([k]) => k)
).map(([k, v]) => 
    [k, v.length > 1 ? v.map(([,v]) => v) : v[0][1]]
));
console.log(result);

© www.soinside.com 2019 - 2024. All rights reserved.