user13700773
user13700773

Reputation: 23

Filtering a large javascript object recursively

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

Answers (2)

Dmytro Biletskyi
Dmytro Biletskyi

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

trincot
trincot

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

Related Questions