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

时间:2019-12-17 17:41:02

标签: javascript data-transform

我有一个这样的收藏集:

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 }));

6 个答案:

答案 0 :(得分:2)

您要做的工作比减速器要多

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));

答案 1 :(得分:0)

另一种减速器功能

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
}, [])

答案 2 :(得分: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)

答案 3 :(得分: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);

答案 4 :(得分:0)

通常,您不希望有两个不同的属性来列出相似的数据,所以我要做的第一件事是创建一个map()函数,将所有number道具更改为numbers并它们是单项数组。然后,我使用reduce()函数将带有uniqId的obj分组在一起。我本来会保留它,但是由于您希望根据对象使用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)

答案 5 :(得分:0)

这是通过地图执行此操作的简单方法,并且逻辑上符合您的期望输出-las,与您的期望输出相比, 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>