从对象数组中删除重复项,但保留一个属性作为数组

3

我有这样一个集合:

const data = [
{index: 1, number: 's1', uniqId: '123', city: 'LA'},
{index: 2, number: 's2', uniqId: '321', city: 'NY'},
{index: 3, number: 's3', uniqId: '123', city: 'LA'},
{index: 4, number: 's4', uniqId: '111', city: 'TX'},
{index: 5, number: 's5', uniqId: '321', city: 'NY'}
]

我希望将其分组,以获得以下结果:
const data = [
{index: 1, numbers: ['s1', 's3'], uniqId: '123', city: 'LA'},
{index: 2, numbers: ['s2', 's5'], uniqId: '321', city: 'NY'},
{index: 3, number: 's4', uniqId: '111', city: 'TX'},
]

我有一个解决方案,但我认为可以用更简洁的方式实现。我可以只使用Ramda,但是更喜欢使用传统的解决方案。 以下是我的解决方案:
 return Object.values(
    data.reduce((r, e) => {
      const key = `${e.uniqId}|${e.city}`;
      if (!r[key]) {
        r[key] = e;
        if (r[key].numbers && !isEmpty(r[key].numbers)) {
          r[key].numbers.push(e.number);
        } else {
          r[key].numbers = [];
          r[key].numbers.push(e.number);
        }
      } else if (r[key].numbers && !isEmpty(r[key].numbers)) {
        r[key].numbers.push(e.number);
      } else {
        r[key].numbers = [];
        r[key].numbers.push(e.number);
      }
      return r;
    }, {}),
  ).map((item, index) => ({ ...item, index: index }));

index 是否意味着某种排序? - igg
这个回答解决了你的问题吗?使用对象对数组项进行分组 - Heretic Monkey
6个回答

2

您在reducer中做的工作比必要的多得多。

const data = [
{index: 1, number: 's1', uniqId: '123', city: 'LA'},
{index: 2, number: 's2', uniqId: '321', city: 'NY'},
{index: 3, number: 's3', uniqId: '123', city: 'LA'},
{index: 4, number: 's4', uniqId: '111', city: 'TX'},
{index: 5, number: 's5', uniqId: '321', city: 'NY'}
]

const reduced = data.reduce((acc, e) => {
  const key = `${e.uniqId}|${e.city}`;
  if (! (key in acc)) {
    acc[key] = Object.assign({}, e);
    delete acc[key]['number'];
    acc[key]['numbers'] = [];
  }
  acc[key]['numbers'].push(e.number);
  return acc;
}, {});

console.log(Object.values(reduced));

0

是的,您可以使用一个reducer和循环条件来完成它,这样比获取对象的键然后遍历它要快。

const data = [
  {index: 1, number: 's1', uniqId: '123', city: 'LA'},
  {index: 2, number: 's2', uniqId: '321', city: 'NY'},
  {index: 3, number: 's3', uniqId: '123', city: 'LA'},
  {index: 4, number: 's4', uniqId: '111', city: 'TX'},
  {index: 5, number: 's5', uniqId: '321', city: 'NY'}
]


const reducer = (accum, cv, currentIndex, source) => {
  const hasValue = accum.some(entry => entry.uniqId === cv.uniqId);
  // we already proccessed it.
  if (hasValue) return accum;

  // we create an object with the desired structure.
  const {
    index,
    uniqId,
    city,
    number
  } = cv;
  let newObj = {
    index,
    uniqId,
    city,
    numbers: [number]
  };

  //now lets fill the numbers :)
  source.forEach((v, index) => {
    //index !== currentIndex &&
    if (index !== currentIndex && v.uniqId === uniqId) {
      newObj['numbers'].push(v.number);
    }
  })

  return [...accum, newObj];

}

const result = data.reduce(reducer, []);

console.log(result)


0
这是一个非常简洁的解决方案,使用数组中的第一个对象作为映射和对象解构。

const data = [
{index: 1, number: 's1', uniqId: '123', city: 'LA'},
{index: 2, number: 's2', uniqId: '321', city: 'NY'},
{index: 3, number: 's3', uniqId: '123', city: 'LA'},
{index: 4, number: 's4', uniqId: '111', city: 'TX'},
{index: 5, number: 's5', uniqId: '321', city: 'NY'}
]

const result = data.reduce((acc, {number,uniqId,city}) => {
  if (!acc[0][uniqId]) {
    acc.push(acc[0][uniqId] = {index: acc.length, numbers: [], uniqId, city});
  }
  acc[0][uniqId].numbers.push(number);
  return acc;
}, [{}]).slice(1);

console.log(result);


0
通常情况下,您不希望有两个列出类似数据的不同属性,因此我首先创建了一个map()函数,将所有number属性更改为numbers并将它们变成单个项目数组。然后我使用reduce()函数将具有唯一ID的对象分组在一起。本来我会就此结束,但由于您想要一个具有numbernumbers结果的对象,因此我编写了一个简单的map()函数,在最后将其转换回这种格式。

const data = [
{index: 1, number: 's1', uniqId: '123', city: 'LA'},
{index: 2, number: 's2', uniqId: '321', city: 'NY'},
{index: 3, number: 's3', uniqId: '123', city: 'LA'},
{index: 4, number: 's4', uniqId: '111', city: 'TX'},
{index: 5, number: 's5', uniqId: '321', city: 'NY'}
]


let res = data.map((el) => {
   el.numbers = [el.number]
   delete el.number
   return el
}).reduce((acc,cur) => {
   let ids = acc.map(obj => obj.uniqId)
   let io = ids.indexOf(cur.uniqId)
   if(io > -1){
      acc[io].numbers.push(cur.numbers[0])
   }else{
      acc.push(cur)
   }
   
   return acc
},[])

console.log(res)

res = res.map(el => {
   if(el.numbers.length <= 1){
      el.number = el.numbers[0]
      delete el.numbers
   }
   return el
})

console.log(res)


0

这里有一个使用Map的简单方法,符合您所需的逻辑输出 - 不幸的是,与您所需的输出相比,numbers属性位置不正确。

如果这很重要,我会留给您来解决 ;)

const data = [
  { index: 1, number: 's1', uniqId: '123', city: 'LA' },
  { index: 2, number: 's2', uniqId: '321', city: 'NY' },
  { index: 3, number: 's3', uniqId: '123', city: 'LA' },
  { index: 4, number: 's4', uniqId: '111', city: 'TX' },
  { index: 5, number: 's5', uniqId: '321', city: 'NY' }
];

const reducer = (acc, e, idx, arr) => {
      const key = `${e.uniqId}|${e.city}`;
      let value = acc.get(key);
      if (value === undefined) {  
       value = Object.assign({},e);    
        acc.set(key, value);
        value.index = acc.size;
      } else {
        if('number' in value) {
         value.numbers = [value.number]
          delete value.number;
     }
        value.numbers.push(e.number);
      }
      if (++idx === arr.length) {
        return Array.from(acc.values());
      }
      return acc;
    };
    
const result = data.reduce(reducer, new Map());
document.getElementById('result').innerText = JSON.stringify(result);
<code id="result"></code>


0

备用的 reducer 函数

data.reduce((acc, curr) => {
  const existingIdRow = acc.find(existingElement => existingElement.uniqId === curr.uniqId);
  if(existingIdRow && existingIdRow.numbers)
    existingIdRow.numbers.push(curr.number);
  else {
    const { uniqId, city } = curr;
    acc.push({index: acc.length + 1, numbers: [curr.number], uniqId, city});
  }
  return acc
}, [])

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