Reputation: 27
I have some trouble trying to filter that kind of nested object, i must be wrong but there must be an easier way to do it.
theObject = {
"a": [{"val":"","date":2},{"val":20,"date":2}],
"b": [{"val":"","date":2}],
"c": [{"val":"10","date":1},{"val":20,"date":2},{"val":"30","date":3}]
}
myFilteredObject = {}
What i want to do is to remove from theObject unnecessary data depending on conditions, for example : val != "" or date < 2 plus, i only want the last object. And keep all the good data in myFilteredObject
Example of filtered tab with those two conditions :
myFilteredObject = {
"a": [{"val":20,"date":2}],
"c": [{"val":"30","date":3}]
}
My question : is there a simpler way to write it, here i'm doing two loops of "For... of object.entries"
let theObject = {
"a": [{"val":"","date":2},{"val":20,"date":2}],
"b": [{"val":"","date":2}],
"c": [{"val":"10","date":2},{"val":20,"date":2},{"val":"30","date":1}]
}
let myFilteredObject = {}
const filterFunction = function(){
// i remove all data that do not match my conditions //
for(let [key, value] of Object.entries(tab)){
if(value[value.length-1].val !== "" || value[value.length-1].date < 1){
myFilteredObject[key] = tab[key]
}
// i keep only the last object of each table //
for(let [key, value] of Object.entries(myFilteredObject)){
myFilteredObject[key].splice(0, myFilteredObject[key].length -1)
}
}
}
filterFunction()
console.log("result",myFilteredObject)
Upvotes: 1
Views: 94
Reputation: 943
You can make a configurable function that receives an object containing information on how to validate each field.
If that object doesn't contain a certain field it will consider it valid by default (the || (() => true))
part)
This way when the rules change, you only change/add items in the validators object, leaving the function body intact. So it means there is less coupling between the code and the shape of the object.
const theObject = {
"a": [{"val":"","date":2},{"val":20,"date":2}],
"b": [{"val":"","date":2}],
"c": [{"val":"10","date":1},{"val":20,"date":2},{"val":"30","date":3}]
}
// put the validation rules here for each field
// if a field has no validator it will be considered valid
const validators = {
val: x => x !== "", // for the `val` field to be valid it has to be !== ""
date: x => x >= 2, // for the `date` field to be valid it has to be >= 2
}
// Function that filters
const filterObject = (obj, validators) =>
Object.entries(obj).reduce((acc, [key, array]) => {
const filteredArray = array.filter((arrayItem) =>
Object.entries(arrayItem).every(([k, v]) =>
(validators[k] || (() => true))(v)));
return { ...acc, ...(filteredArray.length && { [key]: filteredArray }) };
}, {});
// enjoy!
console.log(filterObject(theObject, validators));
Upvotes: 1
Reputation: 386560
You could reduce the entries.
const
data = { a: [{ val: "", date: 2 }, { val: 20, date: 2 }], b: [{ val: "", date: 2 }], c: [{ val: "10", date: 1 }, { val: 20, date: 2 }, { val: "30", date: 3 }] },
result = Object.assign({}, ...Object
.entries(data)
.map(([k, v]) => v.reduce((r, o) => {
if (!o.val) return r;
if (o.date > 2) return { [k]: [o] };
(r[k] ??= []).push(o);
return r;
}, {}))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 1
Reputation: 522
Using two for loops is fine. The data is 2 dimensional, so its not bad. Plus, using javascript means that your performance doesnt matter much anyway, unless its awful.
In terms of "simple" below is my style. Just change "true" to your condition. One thing I'd like to point out is to use IIFE without polluting namespace, meaning that this filtering function should not directly access variables, but receive them as arguements. This is also good for code reusing and maintaining
((obj,result)=>{
for (i in Object.keys(obj)){
let a = {}
for (item in obj[i]){
if (true){
a = item;
}
}
result[i] = [a]
}
})(theObject, myFilteredObject)
Upvotes: 0
Reputation: 370689
Take the initial object's entries, map them to an entry array of just the key and the final item in the array, then filter by whether that final item has a value and a good date:
const theObject = {
"a": [{"val":"","date":2},{"val":20,"date":2}],
"b": [{"val":"","date":2}],
"c": [{"val":"10","date":1},{"val":20,"date":2},{"val":"30","date":3}]
}
const filteredObject = Object.fromEntries(
Object.entries(theObject)
.map(([key, subarr]) => [key, [subarr[subarr.length - 1]]])
.filter(([key, subarr]) => subarr[0].val && subarr[0].date >= 2)
);
console.log(filteredObject);
Upvotes: 2