javascript 具有多个相同属性的过滤数组

f0ofjuux  于 2023-06-28  发布在  Java
关注(0)|答案(6)|浏览(123)

所以我有一个这样的值数组

const foodsList = [
  {
    foodOrigin: "Padang",
    foodName: "Nasi Padang",
    originCode: "PDN"
  },
  {
    foodOrigin: "Padang",
    foodName: "Gulai",
    originCode: "PDN"
  },
  {
    foodOrigin: "Padang",
    foodName: "Rendang",
    originCode: "PDN"
  },
  {
    foodOrigin: "Palembang",
    foodName: "Pempek",
    originCode: "PLG"
  },
  {
    foodOrigin: "Palembang",
    foodName: "Tekwan",
    originCode: "PLG"
  },
  {
    foodOrigin: "Yogyakarta",
    foodName: "Gudeg",
    originCode: "YKT"
  }
];

我想对数组进行过滤,结果会是这样的

const filteredFoodsList = [
  {
    foodOrigin: "Padang",
    originCode: "PDN"
  },
  {
    foodOrigin: "Palembang",
    originCode: "PLG"
  },
  {
    foodOrigin: "Yogyakarta",
    originCode: "YKT"
  }
];

为了达到这个效果,我试着像下面这样做,但有没有更干净更好的方法来做到这一点?(特别是因为我的实际数组的数据由500多个组成)

const filteredFoodsList = [];
for (let i = 0; i < foodsList.length; i++) {
  if (i === 0) {
    filteredFoodsList.push({
      originCode: foodsList[i].originCode,
      foodOrigin: foodsList[i].foodOrigin
    });
  }
  let isExist = false;
  for (let j = 0; j < filteredFoodsList.length; j++) {
    if (foodsList[i].originCode === filteredFoodsList[j].originCode) {
      isExist = true;
    }
  }
  if (!isExist) {
    filteredFoodsList.push({
      originCode: foodsList[i].originCode,
      foodOrigin: foodsList[i].foodOrigin
    });
  }
}
wztqucjr

wztqucjr1#

正如OP所述,应该关注性能,所以...
Map可能是收集唯一值的最快方法。
(我从Nina的答案中借用了Array.from(map, ([originCode, foodOrigin]) => ({ foodOrigin, originCode })),非常优雅)。
但它不会影响性能,因为生成的数组应该很小。
但是她忘了包括一个Map::has()检查,这确实显著提高了性能。
重要的是迭代源数组的速度。像往常一样,我看到不可能有比for(let i = 0; ...)更快的东西...

const foodsList=[{foodOrigin:"Padang",foodName:"Gulai",originCode:"PDN"},{foodOrigin:"Padang",foodName:"Rendang",originCode:"PDN"},{foodOrigin:"Palembang",foodName:"Pempek",originCode:"PLG"},{foodOrigin:"Palembang",foodName:"Tekwan",originCode:"PLG"},{foodOrigin:"Yogyakarta",foodName:"Gudeg",originCode:"YKT"}];

const map = new Map;
for (let i = 0; i < foodsList.length; i++) {
    const item = foodsList[i];
    map.has(item.originCode) || map.set(item.originCode, item.foodOrigin);
}

const result = Array.from(map, ([originCode, foodOrigin]) => ({ foodOrigin, originCode }));

console.log(result);

还有一个基准:

<script benchmark data-count="1">

    const chunk = [{ foodOrigin: "Padang", foodName: "Gulai", originCode: "PDN" }, { foodOrigin: "Padang", foodName: "Rendang", originCode: "PDN" }, { foodOrigin: "Palembang", foodName: "Pempek", originCode: "PLG" }, { foodOrigin: "Palembang", foodName: "Tekwan", originCode: "PLG" }, { foodOrigin: "Yogyakarta", foodName: "Gudeg", originCode: "YKT" }];
    var foodsList = [];
    let count = 10000000;
    while (count--) {
        foodsList.push(...chunk);
    }
    
    // @benchmark Peter's solution

    foodsList
      .reduce((collector, { foodOrigin, originCode = null }) => {

        if (originCode !== null && !collector.lookup.has(foodOrigin)) {

          collector.lookup.set(foodOrigin, true);
          collector.result.push({
            foodOrigin,
            originCode,
          });
        }
        return collector;

      }, { lookup: new Map, result: [] }).result;

    // @benchmark Nina's solution
    Array.from(
        foodsList.reduce((m, { foodOrigin, originCode }) => m.set(foodOrigin, originCode), new Map),
        ([foodOrigin, originCode]) => ({ foodOrigin, originCode })
    );

    // @benchmark Alexander's solution
    const map = new Map;
    for (let i = 0; i < foodsList.length; i++) {
        const item = foodsList[i];
        map.has(item.originCode) || map.set(item.originCode, item.foodOrigin);
    }

    Array.from(map, ([originCode, foodOrigin]) => ({ foodOrigin, originCode }));

    // @benchmark Nina's improved

    Array.from(
        foodsList.reduce((m, { foodOrigin, originCode }) => {
            m.has(foodOrigin) || m.set(foodOrigin, originCode);
            return m;
        }, new Map),
        ([foodOrigin, originCode]) => ({ foodOrigin, originCode })
    );

</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
eufgjt7s

eufgjt7s2#

可以使用对象来存储值

const foodsList = [{
  foodOrigin: 'Padang',
  foodCode: 'SPI',
  foodName: 'Nasi Padang',
}, {
  foodOrigin: 'Padang',
  foodName: 'Gulai',
  originCode: 'PDN',
}, {
  foodOrigin: 'Padang',
  foodName: 'Rendang',
  originCode: 'PDN',
}, {
  foodOrigin: 'Palembang',
  foodName: 'Pempek',
  originCode: 'PLG',
}, {
  foodOrigin: 'Palembang',
  foodName: 'Tekwan',
  originCode: 'PLG',
}, {
  foodOrigin: 'Yogyakarta',
  foodName: 'Gudeg',
  originCode: 'YKT',
}]

const items = {}

for (const item of foodsList) {
  if (item.originCode && !(item.originCode in items)) {
    items[item.originCode] = item
  }
}

const result = Object.values(items).map(({ originCode, foodName }) => ({ originCode, foodName }))

console.log(result)
qeeaahzv

qeeaahzv3#

你可以取一个Map并从中得到一个新数组。

const
    foodsList = [{ foodOrigin: 'Padang', foodName: 'Gulai', originCode: 'PDN' }, { foodOrigin: 'Padang', foodName: 'Rendang', originCode: 'PDN' }, { foodOrigin: 'Palembang', foodName: 'Pempek', originCode: 'PLG' }, { foodOrigin: 'Palembang', foodName: 'Tekwan', originCode: 'PLG' }, { foodOrigin: 'Yogyakarta', foodName: 'Gudeg', originCode: 'YKT' }],
    result = Array.from(
        foodsList.reduce((m, { foodOrigin, originCode }) => m.set(foodOrigin, originCode), new Map),
        ([foodOrigin, originCode]) => ({ foodOrigin, originCode })
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
vkc1a9a2

vkc1a9a24#

以下是我的“老式”方法,不使用Map

const foodsList = [ { foodOrigin: "Padang", foodCode: "SPI", foodName: "Nasi Padang" }, { foodOrigin: "Padang", foodName: "Gulai", originCode: "PDN" }, { foodOrigin: "Padang", foodName: "Rendang", originCode: "PDN" }, { foodOrigin: "Palembang", foodName: "Pempek", originCode: "PLG" }, { foodOrigin: "Palembang", foodName: "Tekwan", originCode: "PLG" }, { foodOrigin: "Yogyakarta", foodName: "Gudeg", originCode: "YKT" }];

const filteredFoodsList = foodsList.reduce(
  (acc, curr) =>
    curr.originCode &&
    !acc.some(a => a?.originCode === curr.originCode)
      ? [...acc, curr]
      : acc,
  []
);

console.log(filteredFoodsList);
brccelvz

brccelvz5#

OP不执行filter,因为不仅数组受影响,而且数组项的每个数据结构也受影响。它也不是map ping,后者将允许后者(更改项的数据),但始终确保数组项的数量相同。
过滤和修改数据的组合是一个经典的reduce任务。
至于OP的问题,不仅需要从源数组中提取唯一的项目数据,还需要实现一个更防弹的解决方案,因为可能会出现不正确的数据,如...

{ foodOrigin: "Padang", foodCode: "SPI", foodName: "Nasi Padang"` }

...与它的正确符号...

{ foodOrigin: "Padang", foodName: "Nasi Padang", originCode: "SPI" }

为了达到最少的迭代工作量,可以实现一个reduce任务,方法是将一个基于对象的初始值作为reduce方法的第二个参数传递,该初始值具有两个键值对,其中lookup-key保存Map-reference,result-key引用数组。
在每次迭代中,回调reducer函数接收这样一个对象作为其第一个参数。它还必须返回这样一个对象,要么对其数据进行更改,要么不进行更改。对象的Map引用将被用作查找特定数据是否已经被处理的最快可能方法之一。该实现将使用map的has方法。
如果感兴趣的数据项尚未被处理,则可以在基于Maplookupset项特定条目。此外,还将创建一个新的、目标数据特定的对象,并将其推入result数组。
后者还以reduce任务的返回值为最终结果。

const foodsList = [{
  foodOrigin: "Padang", foodCode: "SPI", foodName: "Nasi Padang"
  // and not ...
  // // foodOrigin: "Padang", foodName: "Nasi Padang", originCode: "SPI",
}, {
  foodOrigin: "Padang", foodName: "Gulai", originCode: "PDN",
}, {
  foodOrigin: "Padang", foodName: "Rendang", originCode: "PDN",
}, {
  foodOrigin: "Palembang", foodName: "Pempek", originCode: "PLG",
}, {
  foodOrigin: "Palembang", foodName: "Tekwan", originCode: "PLG",
}, {
  foodOrigin: "Yogyakarta", foodName: "Gudeg", originCode: "YKT",
}];

const filteredFoodsList = foodsList
  .reduce((collector, { foodOrigin, originCode = null }) => {

    if (originCode !== null && !collector.lookup.has(foodOrigin)) {

      collector.lookup.set(foodOrigin, true);
      collector.result.push({
        foodOrigin,
        originCode,
      });
    }
    return collector;

  }, { lookup: new Map, result: [] }).result;

console.log({ filteredFoodsList });
.as-console-wrapper { min-height: 100%!important; top: 0; }
nukf8bse

nukf8bse6#

您可以按如下方式使用Array.reduce

const 
      foodsList = [ { foodOrigin: "Padang", foodName: "Nasi Padang", originCode: "PDN" }, { foodOrigin: "Padang", foodName: "Gulai", originCode: "PDN" }, { foodOrigin: "Padang", foodName: "Rendang", originCode: "PDN" }, { foodOrigin: "Palembang", foodName: "Pempek", originCode: "PLG" }, { foodOrigin: "Palembang", foodName: "Tekwan", originCode: "PLG" }, { foodOrigin: "Yogyakarta", foodName: "Gudeg", originCode: "YKT" } ],
      
      filteredFoodsList = foodsList.reduce(
          (acc, {foodOrigin,originCode}) => acc.find(({originCode:oc}) => oc === originCode) ? acc : [...acc, {foodOrigin,originCode}], []
      );

console.log( filteredFoodsList );

相关问题