Как отфильтровать массив объектов на основе некоторых полей?

#javascript #arrays #sorting #ecmascript-6

#javascript #массивы #сортировка #ecmascript-6

Вопрос:

У меня есть массив таких объектов:

 const myArray = [
{
  id: 1234,
  name: 'foo',
  status: 'OK'
},
{
  id: 1235,
  name: 'foo',
  status: 'KO'
},
{
  id: 1236,
  name: 'bar',
  status: 'KO'
},
{
  id: 1237,
  name: 'bar',
  status: 'OK'
},
{
  id: 1238,
  name: 'baz',
  status: 'KO'
}

]

 

и мне нужно отфильтровать его, сохранив только один с тем же именем, и это должен быть тот, у которого самый высокий идентификатор.

 
const expectedOutput = [

{
  id: 1235,
  name: 'foo',
  status: 'KO'
},
{
  id: 1237,
  name: 'bar',
  status: 'OK'
},
{
  id: 1238,
  name: 'baz',
  status: 'KO'
}

]

 

Я боролся, но не могу найти лучшего решения. Есть идеи?

Комментарии:

1. Это должно быть возможно, по крайней мере, с использованием несколько «ручного» метода. Что вы пробовали до сих пор?

2. Комбинация между map, reduce и some, но я не могу найти правильный ключ

3. Если вы включите в свой вопрос, будет намного легче выяснить, где вы ошиблись.

Ответ №1:

Отслеживайте максимальные значения в объекте, сопоставляющем имена объектам:

 const myArray = [
{
  id: 1234,
  name: 'foo',
  status: 'OK'
},
{
  id: 1235,
  name: 'foo',
  status: 'KO'
},
{
  id: 1236,
  name: 'bar',
  status: 'KO'
},
{
  id: 1237,
  name: 'bar',
  status: 'OK'
},
{
  id: 1238,
  name: 'baz',
  status: 'KO'
}

];

const maxes = {};
for (const ele of myArray) {
    if (!(ele.name in maxes) || ele.id > maxes[ele.name].id) {
        maxes[ele.name] = ele;
    }
}
const filtered = Object.values(maxes);
console.log(filtered); 
 .as-console-wrapper {min-height: 100%;} 

Ответ №2:

Вы можете использовать reduce следующим образом. Таким образом, он будет работать как для отсортированного, так и для несортированного массива.

 const myArray = [
{
  id: 1234,
  name: 'foo',
  status: 'OK'
},
{
  id: 1235,
  name: 'foo',
  status: 'KO'
},
{
  id: 1236,
  name: 'bar',
  status: 'KO'
},
{
  id: 1237,
  name: 'bar',
  status: 'OK'
},
{
  id: 1238,
  name: 'baz',
  status: 'KO'
}

];

const ret = myArray.reduce((acc, curr) => {
    const index = acc.findIndex(item => item.name === curr.name);
    if(index> -1 amp;amp; acc[index].id < curr.id) {
       acc[index] = curr;
    } else {
       acc.push(curr);
    }
    return acc;
}, []);

console.log(ret); 

Хотя это будет работать довольно хорошо, поскольку вам нужно перебирать массив только один раз. Но если вы используете цикл for вместо reduce. Это будет намного быстрее, поскольку циклы for обычно быстрее, чем map, filter, reduce и т. Д. Для достижения самого быстрого результата вы можете сделать следующее,

  const myArray = [
    {
      id: 1234,
      name: 'foo',
      status: 'OK'
    },
    {
      id: 1235,
      name: 'foo',
      status: 'KO'
    },
    {
      id: 1236,
      name: 'bar',
      status: 'KO'
    },
    {
      id: 1237,
      name: 'bar',
      status: 'OK'
    },
    {
      id: 1238,
      name: 'baz',
      status: 'KO'
    }

    ];

    let ret = [];
    
    for(let i =0;i<myArray.length; i  ) {
        const index = ret.findIndex(item => item.name === myArray[i].name);
        if(index > -1 amp;amp; ret[index].id < myArray[i].id) {
            ret[index]=myArray[i];
        } else {
            ret.push(myArray[i]);
        }
    }

    console.log(ret); 

Ответ №3:

Поскольку массив уже отсортирован по id , вы можете использовать Map объект и просто задать каждое значение, используя ключ name as. Переопределение предыдущего значения, если оно присутствует. Обратите внимание, что это соответствует требованиям только до тех пор, пока последний элемент с определенным именем также имеет наибольшее значение.

 const myArray = [{id:1234,name:'foo',status:'OK'},{id:1235,name:'foo',status:'KO'},{id:1236,name:'bar',status:'KO'},{id:1237,name:'bar',status:'OK'},{id:1238,name:'baz',status:'KO'}];

const lookup = new Map();
myArray.forEach(item => lookup.set(item.name, item));
const result = Array.from(lookup.values());
console.log(result); 

Порядок результирующих элементов основан на порядке вставки в Map объект. Первый вставленный ключ будет первым элементом результирующего массива. Второй вставленный ключ будет вторым элементом и т.д.

Ответ №4:

Вы могли бы сделать это с помощью Map Object .

  • Сначала создайте новый объект Map
  • Пройдите по массиву с помощью forEach() метода.
  • Поместить name в качестве ключа в переменную с именем key
  • Проверьте, существует ли ключ, используя has(key) метод в Map Object именованном map
  • Если ключ не существует, установите его в объект Map, вызвав set(key, value) метод. В этом решении ключ — это имя, а значение — объект.
  • Если ключ существует, то получите объект с помощью get(key) метода, получите максимальный идентификатор с помощью Math.max() метода, затем обновите объект и установите его в объект карты.
 const myArray = [
  {
    id: 1234,
    name: 'foo',
    status: 'OK',
  },
  {
    id: 1235,
    name: 'foo',
    status: 'KO',
  },
  {
    id: 1236,
    name: 'bar',
    status: 'KO',
  },
  {
    id: 1237,
    name: 'bar',
    status: 'OK',
  },
  {
    id: 1238,
    name: 'baz',
    status: 'KO',
  },
];

const map = new Map();
myArray.forEach((x) => {
  const key = x.name;
  if (map.has(key))
    map.set(key, { ...map.get(key), id: Math.max(map.get(key).id, x.id) });
  else map.set(key, { ...x });
});
const ret = [...map.values()];
console.log(ret);