Массив объектов, уменьшаемых по нескольким параметрам

Необходимо уменьшить массив объектов, чтобы вернуть наибольшее значение с тем же именем.

Нужно следующее,

[
    {
        name: 'a',
        value: 20,
        ...(other object values)
    },
    {
        name: 'a',
        value: 80
        ...(other object values)
    },
    {
        name: 'b',
        value: 90,
        ...(other object values)
    },
    {
        name: 'b',
        value: 50,
        ...(other object values)
    }
]

Вернуться

[
    {
        name: 'a',
        value: 80
        ...(other object values)
    },
    {
        name: 'b',
        value: 90,
        ...(other object values)
    }
]

У меня есть решение, но оно кажется слишком сложным. Интересно, есть ли более простой способ его достижения?

Ниже приведено решение, о котором я мог подумать:

var toReduce = [
    {
        'name': 'a',
        'value': 20,
        'other': 'any',
    },
    {
        'name': 'a',
        'value': 80,
        'other': 'value',
    },
    {
        'name': 'b',
        'value': 90,
        'other': 'extra',
    },
    {
        'name': 'b',
        'value': 50,
        'other': 'super',
    }
];


function arrayReducer(arrayToReduce) {
    // Created an object separating by name
    let reduced = arrayToReduce.reduce((accumulator, currentValue) => {
        (accumulator[currentValue.name] =
            accumulator[currentValue.name] || []).push(currentValue);
        return accumulator;
    }, {});
    // Reduce object to the highest value
    for (let quotes of Object.keys(reduced)) {
        reduced[quotes] = reduced[quotes].reduce(
            (accumulator, currentValue) => {
                return accumulator && accumulator.value > currentValue.value
                    ? accumulator
                    : currentValue;
            }
        );
    }
    // return only object values
    return Object.values(reduced);
}

console.info(arrayReducer(toReduce));
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
113
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Вы можете вычислить максимум во время reduce(), а не в отдельном цикле.

var toReduce =
  [ { 'name': 'a', 'value': 20 }
  , { 'name': 'a', 'value': 80 }
  , { 'name': 'b', 'value': 90 }
  , { 'name': 'b', 'value': 50 }
  ];

function arrayReducer(arrayToReduce) 
  {
  // Created an object separating by name
  let reduced = arrayToReduce.reduce((accumulator, currentValue) => 
    {
    accumulator[currentValue.name] = 
      { name  : currentValue.name
      , value : accumulator.hasOwnProperty(currentValue.name) 
                ? Math.max(accumulator[currentValue.name].value, currentValue.value) 
                : currentValue.value
      };
    return accumulator;
    }
    , {});
  // return only object values
  return Object.values(reduced);
  }

console.info(arrayReducer(toReduce));

Спасибо за ваш ответ, я фактически отредактировал свой первоначальный вопрос, поскольку объект может иметь больше значений (динамически), кроме имени и значения, ваш ответ охватывает только объект с двумя значениями.

caiovisk 02.07.2024 03:20

Тот же подход работает для любого количества свойств.

Barmar 02.07.2024 03:56

Просто скопируйте их все в аккумулятор.

Barmar 02.07.2024 03:57

Если ваши изменения делают ответы недействительными, вам следует сделать это как новый вопрос. Несправедливо редактировать вопрос после получения действительных ответов на исходный вопрос.

Barmar 02.07.2024 03:58

Согласен, однако я попытался обновить вопросы, поскольку все ответы были ограничены фиксированной структурой объектов... Обновление вопроса принесет пользу всем, кто преследует этот подход.

caiovisk 03.07.2024 02:11
Ответ принят как подходящий

Самый простой здесь, вероятно, простой цикл:

const result = {};

for (const item of input) {
  if (item.value > (result[item.name]?.value ?? 0)) {
    result[item.name] = item;
  }
}

// To turn it back into the original array format:
const output = Object.values(result)

Конечно, вы можете превратить это в одно утверждение с помощью reduce() и так далее, но разборчивость пострадает, как вы можете видеть из всех остальных ответов.

Но вот версия .reduce(), использующая тот же подход:

const output = Object.values(
  input.reduce( (acc, cur) => {
    if (cur.value > (acc[cur.name]?.value ?? 0)) {
      acc[cur.name] = cur;
    } 
    return acc;
  }, {})
)

Спасибо за ваш ответ, я фактически отредактировал свой первоначальный вопрос, поскольку объект может иметь больше значений (динамически), помимо имени и значения, ваш ответ охватывает только объект с двумя значениями.

caiovisk 02.07.2024 03:21

Это не работает, если значение может быть отрицательным.

Barmar 02.07.2024 04:00

@Barmar, если вам нужны отрицательные значения, используйте -Infinity вместо 0

Evert 02.07.2024 05:20

Это может помочь. Но это не за один раз.

var toReduce = [
    {
        name: "a",
        value: 20,
    },
    {
        name: "a",
        value: 80,
    },
    {
        name: "b",
        value: 90,
    },
    {
        name: "b",
        value: 50,
    },
];

const itemByMaxValue = toReduce.reduce((acc, item) => {
    if (acc[item.name]) {
        acc[item.name] = Math.max(item.value, acc[item.name]);
    } else {
        acc[item.name] = item.value;
    }
    return acc;
}, {});

const result = Object.entries(itemByMaxValue).map(([name, value]) => ({
    name,
    value,
}));

console.info(result);

Другой вариант — использовать Map.groupBy() для группировки каждого объекта по name. После группировки вы можете перебирать карту и для каждой группы объектов захватывать максимум value (это делается с помощью Array.from() на карте с функцией сопоставления ниже):

const arr = [ { name: 'a', value: 20 }, { name: 'a', value: 80 }, { name: 'b', value: 90 }, { name: 'b', value: 50 } ];

const res = Array.from(
  Map.groupBy(arr, obj => obj.name),
  ([name, grouped]) => ({name, value: Math.max(...grouped.map(obj => obj.value))}) 
);
console.info(res);

Если ваши объекты могут иметь другие ключи/свойства, вы можете найти максимальный объект из каждой группы и использовать его:

const arr =  [{ 'name': 'a', 'value': 20, 'other': 'any', }, { 'name': 'a', 'value': 80, 'other': 'value', }, { 'name': 'b', 'value': 90, 'other': 'extra', }, { 'name': 'b', 'value': 50, 'other': 'super', } ];

const findMax = arr => arr.reduce((max, curr) => curr.value > max.value ? curr : max);
const res = Array.from(
  Map.groupBy(arr, obj => obj.name).values(),
  findMax 
);
console.info(res);

Спасибо за ваш ответ, я фактически отредактировал свой первоначальный вопрос, поскольку объект может иметь больше значений (динамически), помимо имени и значения, ваш ответ охватывает только объект с двумя значениями.

caiovisk 02.07.2024 03:21

@caiovisk Вместо запуска Math.max() вы можете использовать цикл (например, сокращение), чтобы найти максимальный объект и использовать его вместо этого. Смотрите обновленный ответ.

Nick Parsons 02.07.2024 03:45

Функция keep, приведенная ниже, представляет собой многоразовый способ свести список элементов на карту по их ключам и сравнить их значения с помощью компаратора.

Для параметров key и value вы можете передать метод доступа (получатель) или индекс (поле), чтобы сообщить функции, как определить, как «получить» значение из элемента.

Все это происходит за время O(n).

const original = [
  { name: 'a', value: 20 },
  { name: 'a', value: 80 },
  { name: 'b', value: 90 },
  { name: 'b', value: 50 }
];

const get = (item, keyOrFn) => {
  if (!keyOrFn) return item;
  return typeof keyOrFn === 'function'
    ? keyOrFn(item)
    : item[keyOrFn];
};

const keep = (arr, comparator, { key, value }) => {
  return [...arr.reduce((acc, item) => {
    const k = get(item, key);
    const existing = acc.get(k);
    if (!existing) return acc.set(k, item);
    const a = get(item, value);
    const b = get(existing, value);
    if (comparator(a, b) > 0) return acc.set(k, item);
    return acc;
  }, new Map()).values()];
};

const maxValues = keep(
  original,
  (a, b) => a - b,
  { key: 'name', value: 'value' },
  // OR: { key: x => x.name, value: x => x.value }
);

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

попробуй это

const data = [
    {
        name: 'a',
        value: 20,
        ...(other object values)
    },
    {
        name: 'a',
        value: 80
        ...(other object values)
    },
    {
        name: 'b',
        value: 90,
        ...(other object values)
    },
    {
        name: 'b',
        value: 50,
        ...(other object values)
    }
]

const obj = Object.groupBy(data, ({name}) => name)
const result = Object.keys(obj).map(key => {
  const max = obj[key].toSorted((a, b) => b.value - a.value)[0]
  return max
})

console.info(result)

Хорошее использование groupBy

slebetman 02.07.2024 04:38

Простой способ добиться этого:

const newObj = [toReduce[0]];
const objKeyArray = [toReduce[0].name];


for (i = 1; i < toReduce.length; i++) {
  for (j = 0; j < newObj.length; j++) {
    if (toReduce[i].name === newObj[j].name) {
        // If you just want to change the data of the specific property
      //   newObj[j].value = Math.max(toReduce[i].value, newObj[j].value);
      // If you want to replace the object.
      newObj[j] = toReduce[i];
    } else {
      if (!objKeyArray.includes(toReduce[i].name)) {
        objKeyArray.push(toReduce[i].name);
        newObj.push(toReduce[i]);
      }
    }
  }
}
console.info(newObj);

Другие вопросы по теме