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:
data.filter
applies the callback to each element of the arraydata
names.find
applies the callback to each element of the arraynames
in order to find a match.!!
is used becausefind
returns an element, so!!
causes a double negation that turns it into a boolean, the expected return from thefind
callback (its actually not strictly necessary, asfind
returningundefined
would be coerced to false), but it clarifies the intention.- The result is only the elements of
data
that have names properties that match the values innames
.
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.
- Identifying the data item by its name (from the additionally provided
names
array). - Filtering each data item's
points
array by whether apoints
item's first array item (thenumber
value) is within a certain number range (which is defined by the additionally providedstartnumber
andendnumber
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 aMap
instance) for the to be explicitly processeddata
items. - the
startnumber
andendnumber
range values aslowerNumber
respectivelyupperNumber
. - and a
result
array where only thosedata
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; }