Reputation: 23
I am filtering a large javascript object (JSON file of 37000 lines) recursively in react. After i have filtered the object i send it down as props to a component that renders the object.
This is my current function and its working fine for all of the smaller files, but its pretty slow for the larger one's. Are there any improvements i could make to the function to make it perform faster?
export const filterBySubstringKey = (data: any, value: string) => {
const iterate = (object: any, result: any) => {
return Object.keys(object).reduce((acc, key) => {
let tempResult = {};
if (key.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
result[key] = object[key];
return true;
}
if (object[key] !== null && typeof object[key] === 'object' && iterate(object[key], tempResult)) {
result[key] = tempResult;
return true;
}
return acc;
}, false);
};
let result = {};
iterate(data, result);
return result;
};
Upvotes: 2
Views: 410
Reputation: 1903
I created 3 variants. I slightly simplified your code. Here are performance results jsperf. Results differ from run to run, but variant 4 is the fastest
Edit: Added some more optimization in v5, it outperforms all other.
const filterBySubstringKey5 = (data, value) => {
const valueLow = value.toLowerCase()
const iterate = object => {
let res = {};
for (const key in object) {
const ok = object[key]
if (typeof ok === "object") {
res[key] = iterate(ok);
} else {
if (key.toLowerCase().indexOf(valueLow) !== -1) {
res[key] = ok;
}
}
}
return res;
};
return iterate(data);
};
const filterBySubstringKey2 = (data, value) => {
const iterate = object => {
return Object.entries(object).reduce((acc, [key, val]) => {
if (typeof val === "object") {
acc[key] = iterate(val);
} else {
if (key.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
acc[key] = val;
}
}
return acc;
}, {});
};
return iterate(data);
};
const filterBySubstringKey3 = (data, value) => {
const iterate = object => {
return Object.keys(object).reduce((acc, key) => {
if (typeof object[key] === "object") {
acc[key] = iterate(object[key]);
} else {
if (key.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
acc[key] = object[key];
}
}
return acc;
}, {});
};
return iterate(data);
};
const filterBySubstringKey4 = (data, value) => {
const iterate = object => {
let res = {};
for (const key in object) {
if (typeof object[key] === "object") {
res[key] = iterate(object[key]);
} else {
if (key.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
res[key] = object[key];
}
}
}
return res;
};
return iterate(data);
};
Upvotes: 1
Reputation: 350270
There is not much you can do, but I would suggest the following, which may slightly improve the timings:
Don't pass the second argument to iterate
, but make it that the return value is the object you want to get from it.
Use oldfashioned for
loops instead of array methods.
Like in comments you already said you did: call value.toLowerCase()
only once.
Read from object[key]
only once
Use includes
instead of indexOf
export const filterBySubstringKey = (data: any, value: string) => {
value = value.toLowerCase();
const iterate = (object: any) => {
let result = {};
for (let key in object) { // Assuming no enumerable inherited props
let val = object[key];
if (key.toLowerCase().includes(value)) {
result[key] = val;
} else if (val !== null && typeof val === 'object') {
let temp = iterate(val);
if (temp) result[key] = temp;
}
}
for (let key in result) return result; // When result is not empty
// default: return undefined
};
return iterate(data);
};
Upvotes: 1