使用对象键对对象数组进行分组

Group array of objects with object keys

在原版中根据键对对象数组进行分组的最佳方法是什么 javascript,假设我有 10000 条记录,这是示例对象

[
 {
  company: "TATA",
  car: "TATA Indica",
  color: "Blue"
 },
 {
  company: "TATA",
  car: "TATA Indica",
  color: "Black"
 },
 {
  company: "TATA",
  car: "Safari",
  color: "Blue"
 },
 {
   "company": "Suzuki",
    car: "",
    color: ""
 }
]

预期输出为

{
   "company": ["TATA", "Suzuki"],
   "car": ["TATA Indica", "Safari"],
   "color": ["Blue", "Black"]
}

你可以在这里使用reduce here, and to optimize it you can use Map

const arr = [{
    company: "TATA",
    car: "TATA Indica",
    color: "Blue",
  },
  {
    company: "TATA",
    car: "TATA Indica",
    color: "Black",
  },
  {
    company: "TATA",
    car: "Safari",
    color: "Blue",
  },
  {
    company: "Suzuki",
    car: "",
    color: "",
  },
];

const result = arr.reduce((acc, curr) => {
  Object.keys(curr).forEach((k) => {
    if (curr[k]) {
      if (!acc[k]) acc[k] = new Set();
      else acc[k].add(curr[k]);
    }
  });
  return acc;
}, {});

Object.keys(result).forEach((k) => (result[k] = [...result[k]]));
console.log(result);

您可以使用 Set 来存储每个键的唯一值并稍后将它们转换回数组

const data = [
  {
    company: "TATA",
    car: "TATA Indica",
    color: "Blue",
  },
  {
    company: "TATA",
    car: "TATA Indica",
    color: "Black",
  },
  {
    company: "TATA",
    car: "Safari",
    color: "Blue",
  },
  {
    company: "Suzuki",
    car: "",
    color: "",
  },
]

let res = data.reduce((acc, el) => {
  for (const [key, value] of Object.entries(el)) {
    if (!value) continue

    if (key in acc) {
      const set = new Set(acc[key])
      set.add(value)
      acc[key] = Array.from(set)
    } else {
      acc[key] = [value]
    }
  }

  return acc
}, {})

console.log(res)

对于原始值,您可以轻松使用 Set and Map 的组合。 您使用 Map 作为键,使用 Set 作为值。

const companies = ["TATA", "Suzuki", "Škoda", ""];
const cars = ["TATA Indica", "Safari", "Fabia", ""];
const colors = ["Blue", "Black", "Red", "Yellow", ""]
const arr = Array.from({length: 1000}, (v, i) => ({
  company: companies[i % companies.length],
  car: cars[i % cars.length],
  color: colors[i % colors.length]
}));

console.time('Operation');
const map = new Map();
for(let item of arr) {
  for(let key of Object.keys(item)){
    let keySet = map.get(key);
    if(!keySet){
      keySet = new Set();
      map.set(key, keySet);
    }
    const value = item[key];
    if(value !== '') {
      keySet.add(item[key]);
    }
  }
}
const result = {};
for(let key of map.keys()) {
  result[key] = Array.from(map.get(key));
}
console.timeEnd('Operation');
console.log(result);

看起来您使用的数据始终具有稳定的形状。在这种情况下,您可以预先声明结果对象的形状,然后简单地将数据累加到其中,只检查非空值。这里使用Map and Set进行累加,然后映射到最终的结果对象。

const data = [{ company: 'TATA', car: 'TATA Indica', color: 'Blue' }, { company: 'TATA', car: 'TATA Indica', color: 'Black' }, { company: 'TATA', car: 'Safari', color: 'Blue' }, { company: 'Suzuki', car: '', color: '' },];

const map = new Map(Object.keys(data[0]).map((k) => [k, new Set()]));

for (const d of data) {
  for (const k of map.keys()) {
    if (d[k] !== '') {
      map.get(k).add(d[k]);
    }
  }
}

const result = {};
for (const k of map.keys()) {
  result[k] = Array.from(map.get(k));
}

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

奇怪的是,看似最幼稚的实现实际上证明相当高效(根据 @Totati's benchmark)。连续 map().filter() 次调用。

const data = [{ company: 'TATA', car: 'TATA Indica', color: 'Blue' }, { company: 'TATA', car: 'TATA Indica', color: 'Black' }, { company: 'TATA', car: 'Safari', color: 'Blue' }, { company: 'Suzuki', car: '', color: '' },];

const result = {};

for (const k of Object.keys(data[0])) {
  result[k] = Array.from(
    new Set(data.map(({ [k]: key }) => key).filter((n) => n !== ''))
  );
}

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