从数组的数组中过滤数据的优雅方式

elegant way of filtering data from array of array

如何从array of array中过滤数据?

请在下面给出的示例中找到解释。

我们必须在数据查询中使用startnumberendnumber

const data =  [
{
  "name": "x",
  "points": [
    [100, 50, 1],        //[number, value, bit]       
    [150, 51, 0],
    [170, 52, 1],
    [200, 53, 0]
  ]
},
{
  "name": "y",
  "points": [
    [60, 50, 1],
    [100, 5, 1],
    [150, 6, 0],
    [170, 7, 1],
    [200, 53, 1]
  ]
},
{
  "name": "z",
  "points": [
    [300, 50, 1],
    [350, 51, 0],
    [370, 52, 1],
    [400, 53, 1]
  ]
}
]

// want to find the records with name equal to x & y and number between 100 to 170

const names = ["x", "y"];
const startnumber = 100;
const endnumber = 170;

const finalResult= [];
for(const n of names){
  console.log('name', n);
  console.log('startnumber', startnumber)
  console.log('endnuumber', endnumber)
  const result = data.find(x=>x.name === n)
  
  // how to use startnumber and endnumber here in above line/query ? OR some other elegant solution is required
  
  if(result){
    finalResult.push('result', result);
  }
}

if(finalResult.length){
  console.log(finalResult);
}

预期结果应该是

[
  {
    "name": "x",
    "points": [
      [100, 50, 1],
      [150, 51, 0],
      [170, 52, 1],
    ]
  },
  {
    "name": "y",
    "points": [
      [100, 5, 1],
      [150, 6, 0],
      [170, 7, 1],
    ]
  }
]
const results = data.filter(elem => !!names.find(name => name == elem.name))

解释:

  1. data.filter 将回调应用于数组的每个元素 data
  2. names.find 将回调应用于数组 names 的每个元素以找到匹配项。
  3. 使用
  4. !! 是因为 find return 是一个元素,所以 !! 会导致双重否定,将其变成布尔值,即预期的 return 来自 find 回调(实际上并非绝对必要,因为 find returning undefined 将被强制为 false),但它阐明了意图。
  5. 结果只有 data 的元素具有与 names 中的值匹配的名称属性。

const data =  [
{
  "name": "x",
  "points": [
    [100, 50, 1],        //[number, value, bit]       
    [150, 51, 0],
    [170, 52, 1],
    [200, 53, 0]
  ]
},
{
  "name": "y",
  "points": [
    [60, 50, 1],
    [100, 5, 1],
    [150, 6, 0],
    [170, 7, 1],
    [200, 53, 1]
  ]
},

{
  "name": "y",
  "points": [
    [60, 50, 1],
    [200, 53, 1]
  ]
},

{
  "name": "z",
  "points": [
    [300, 50, 1],
    [350, 51, 0],
    [370, 52, 1],
    [400, 53, 1]
  ]
}
]

// want to find the records with name equal to x & y and number between 100 to 170

const names = ["x", "y"];
const startNumber = 100;
const endNumber = 170;

const results = data
  .filter(elem => !!names.find(name => name == elem.name))
  .map(elem => {
    return {
      name: elem.name,
      points: elem.points.filter(point => point[0] >= startNumber && point[0] <= endNumber)
    }
  })
  .filter(elem => elem.points.length > 0)

console.log(results)

该任务和两个给定的数据结构意味着每个数据项的双重过滤过程。

  1. 通过名称识别数据项(来自额外提供的 names 数组)。
  2. 根据 points 项的第一个数组项(number 值)是否在某个数字范围内(由额外提供的定义)来过滤每个数据项的 points 数组startnumberendnumber 值)。

因此,一个可行的方法是 reduce 提供的 data 数组由一个缩减器函数处理,每个 data 项目负责上述两个任务。

reducer 函数的 initial value 将是一种配置 and/or 收集器对象,具有...

  • name 查找(作为 Map 实例应用)以显式处理 data 项。
  • startnumberendnumber范围值分别为lowerNumberupperNumber
  • 和一个 result 数组,其中只有那些 data 项被推入其中满足所有要求。

function collectItemsByNameWhichHavePointsInNumberRange(collector, item) {
  const { nameLookup, lowerNumber, upperNumber, result } = collector;
  let { name, points } = item;

  // ... collect items by name ...
  if (nameLookup.has(name)) {
    points = points
      .filter(([number]) =>
        (number >= lowerNumber) && (number <= upperNumber)
      );

    // ... which (actually do) have points (with)in (a) number range.
    // (according to the above `points` filter result)

    if (points.length >= 1) {
      result
        .push({
          ...item,
          points,
        });
    }
  }
  return collector;
}

const data = [{
  name: "x",
  points: [
  //[number, value, bit]
    [100, 50, 1],
    [150, 51, 0],
    [170, 52, 1],
    [200, 53, 0],
  ],
}, {
  name: "y",
  points: [
    [60, 50, 1],
    [100, 5, 1],
    [150, 6, 0],
    [170, 7, 1],
    [200, 53, 1],
  ],
}, {
  name: "z",
  points: [
    [300, 50, 1],
    [350, 51, 0],
    [370, 52, 1],
    [400, 53, 1],
  ],
}];

const names = ["x", "y"];
const startnumber = 100;
const endnumber = 170;

const { result } = data
  .reduce(collectItemsByNameWhichHavePointsInNumberRange, {
    nameLookup: new Map(names.map(value => [value, value])),
    lowerNumber: startnumber,
    upperNumber: endnumber,
    result: [],
  });

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

最优雅的解决方案:

const res = data.reduce((acc, curr) => {
  const { name, points } = curr
  const filteredPoints = points.filter(p => p[0] >= startNumber && p[0] <= endNumber)
  return names.includes(name) && filteredPoints.length > 0 ? acc.concat({ name, points: filteredPoints }) : acc
}, [])