Home > Mobile >  elegant way of filtering data from array of array
elegant way of filtering data from array of array

Time:05-05

How to filter data from array of array?.

pls find the explanation in below given example.

We must use startnumber and endnumber in data query.

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);
}

Expected result should be

[
  {
    "name": "x",
    "points": [
      [100, 50, 1],
      [150, 51, 0],
      [170, 52, 1],
    ]
  },
  {
    "name": "y",
    "points": [
      [100, 5, 1],
      [150, 6, 0],
      [170, 7, 1],
    ]
  }
]

CodePudding user response:

const results = data.filter(elem => !!names.find(name => name == elem.name))

Explanation:

  1. data.filter applies the callback to each element of the array data
  2. names.find applies the callback to each element of the array names in order to find a match.
  3. !! is used because find returns an element, so !! causes a double negation that turns it into a boolean, the expected return from the find callback (its actually not strictly necessary, as find returning undefined would be coerced to false), but it clarifies the intention.
  4. The result is only the elements of data that have names properties that match the values in 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)

CodePudding user response:

The most elegant solution:

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
}, [])

CodePudding user response:

The task and the two given data structures imply a two folded filter process for each data item.

  1. Identifying the data item by its name (from the additionally provided names array).
  2. Filtering each data item's points array by whether a points item's first array item (the number value) is within a certain number range (which is defined by the additionally provided startnumber and endnumber values).

Thus a viable approach was to reduce the provided data array by a reducer function which for each data item takes care of the above two tasks.

The reducer function's initial value will be kind of a config and/or collector object which features ...

  • a name lookup (applied as a Map instance) for the to be explicitly processed data items.
  • the startnumber and endnumber range values as lowerNumber respectively upperNumber.
  • and a result array where only those data items get pushed into which fulfill all requirements.

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; }

  • Related