Создайте массив на основе значений существующего массива объектов в Javascript

#javascript #arrays #sorting #reduce

Вопрос:

У меня есть массив объектов следующим образом.

  const arr = [
    {
      categoryId: "I01",
      categoryName: "Category_one",
      price: 100
    },
    {
      categoryId: "I02",
      categoryName: "Category_two",
      price: 200
    },
    {
      categoryId: "I03",
      categoryName: "Category_three",
      price: 300
    },
    {
      categoryId: "I02",
      categoryName: "Category_two",
      price: 210
    },
    {
      categoryId: "I03",
      categoryName: "Category_three",
      price: 310
    }
 ]
 

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

 arr2 = [
    {
      id: 0,
      categoryId: "I01",
      categoryName: "Category_one",
      minimum_price: 100,
      count: 1
    },
    {
      id: 1,
      categoryId: "I02",
      categoryName: "Category_two",
      minimum_price: 200,
      count: 2
    },
    {
      id: 2,
      categoryId: "I03",
      categoryName: "Category_three",
      minimum_price: 300,
      count: 2
    },
]
 

Я создал результат до уровня ниже

 const resArr = [];
    arr.forEach((item) => {
        let i = resArr.findIndex(x => x.categoryId == item.categoryId);
        if (i <= -1) {
            resArr.push({ categoryId: item.categoryId, categoryName: item.categoryName });
        }
    });
    resArr.forEach((o, i) => o.id = i);
 

И получил такой результат,

  res = [
        {
          id: 0,
          categoryId: "I01",
          categoryName: "Category_one"
        },
        {
          id: 1,
          categoryId: "I02",
          categoryName: "Category_two"
        },
        {
          id: 2,
          categoryId: "I03",
          categoryName: "Category_three"
        },
    ]
 

Мне нужно иметь значение minimum_price и значения количества. высоко ценю ваши предложения.

Ответ №1:

Вы можете использовать Array.prototype.reduce вместе с Object.values:

 let id = -1;

// Even though we're reducing to an object, Object.values will
// extract the result to an array.
const result = Object.values(arr.reduce(({
  categoryId,
  categoryName,
  price,
}, acc) => {
  // If we don't already have something from that
  // category, make a new entry in the hash
  if (!(categoryId in acc)) acc[categoryId] = {
    categoryId,
    categoryName,
    minPrice: price,
    id: id  , // increment the id
    count: 0,
  };

  // Increase the count for that category by 1
  acc[categoryId].count  = 1;

  // Set new minimum price if applicable
  if (acc[categoryId].price > price) acc[categoryId].price = price;

  // return the accumulator
  return acc;
}, {}));
 

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

1. Привет @Джаред Смит, я сверился со своими данными, но не смог получить ожидаемый результат с вашим ответом.

Ответ №2:

Я бы предложил это:

во-первых, примените id поле:

 arr.map(item=>({...item, id: Number(item.categoryId.slice(1))}))
 

И превратите его в (эффективно) Map<int, Category> , вы можете использовать либо a Array , либо a Map , но вы должны знать, что это может быть не тот конечный массив, который вам нужен, потому что он может быть разреженным.

(и вы можете объединить первый шаг с этим):

 const map = [];
for(const item of arr) {
  const id = Number(item.categoryId.slice(1))
  if(!map[id]) {
    map[id] = {
        id,
        categoryId: item.categoryId,
        categoryName: item.categoryName,
        minimum_price: item.price,
        count: 1
    }
  } else {
    map[id].count  = 1
    map[id].minimum_price = Math.min(map[id].minimum_price, item.price)
  }
}
 

И сделайте его конечным массивом, который вы хотите. Вы можете просто отфильтровать пустые слоты:

 return map.filter(Boolean)
 

И я бы настоятельно рекомендовал вам id придерживаться categoryId этого . Наличие sth может сбивать с толку. Нравится {id: 0, categoryId: 'I01'} .

И это то, что вы получите (JSON-строка):

 [{"id":1,"categoryId":"I01","categoryName":"Category_one","minimum_price":100,"count":1},{"id":2,"categoryId":"I02","categoryName":"Category_two","minimum_price":200,"count":2},{"id":3,"categoryId":"I03","categoryName":"Category_three","minimum_price":300,"count":2}]
 

можете ли вы обновить свой ответ, если «идентификатор категории» не содержит никакого целого числа

Это зависит от того, хотите ли вы по-прежнему иметь значимое id поле. Если нет, то карта должна быть Map<string, Category> изображением (или объектом).:

 const map = {};
for(const item of arr) {
  if(!map[item.categoryId]) {
    map[item.categoryId] = {
        categoryId: item.categoryId,
        categoryName: item.categoryName,
        minimum_price: item.price,
        count: 1
    }
  } else {
    map[item.categoryId].count  = 1
    map[item.categoryId].minimum_price = Math.min(map[id].minimum_price, item.price)
  }
}
 

И вам нужно будет превратить карту в массив, что может быть сделано Object.keys , Object.values Object.entries , или for...in циклом:

 Object.values(map)
 

И примените это id поле, если оно вам все еще нужно:

 Object.values(map).map((v, id) => ({...v, id}))
 

Или, если вам нужно содержательное id поле,

Вам нужно будет найти способ каким-то образом отобразить идентификатор категории в целое число.

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

1. Привет, @Yongjian Wang, Ответ вполне одобрительный, но можете ли вы обновить свой ответ, если «Идентификатор категории» не содержит никакого целого числа.