skip to Main Content

Given an object and a filter function, write a function that will go through and filter the object, then return a filtered object.

Function:
deepFilter(obj, filter)

Input Object: 
const obj = {
  a: 1,
  b: {
    c: 2,
    d: -3,
    e: {
      f: {
        g: -4,
      },
    },
    h: {
      i: 5,
      j: 6,
    },
  }
}

Callback Function:
const filter = (n) => n >= 0

Output:
{ a: 1, b: { c: 2, h: { i: 5, j: 6 } } }

My implementation:

function deepFilter(obj, filter, resultObj){
    if(typeof(obj)!=="object"){
        return obj
    }
  for(let key in obj){
    if(obj.hasOwnProperty(key)){
        const filteredValue = deepFilter(obj[key], filter, resultObj)
        if(filter(filteredValue)){
            resultObj[key] = filteredValue;
        }
    }
  }
   return resultObj
}

The output isn’t what is expected.

My output:
{ a: 1, c: 2, i: 5, j: 6 }

Is there a different approach to this problem or is it something that I’m missing in the function implementation?

6

Answers


  1. for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        const value = obj[key];
        if (typeof value === 'object' && value !== null) {
          const filteredValue = deepFilter(value, filter);
          if (Object.keys(filteredValue).length > 0) {
            result[key] = filteredValue;
          }
        } else if (filter(value)) {
          result[key] = value;
        }
      }
    }
    return result;
    };
    Login or Signup to reply.
  2. You could treat every nested object as own object and return only an object if the nested value/objects have wanted values.

    const
        deepFilter = (object, condition) => {
            let result;
            
            for (const key in object) {
                const value = object[key];
                if (value && typeof value === "object") {
                    const nested = deepFilter(value, condition);
                    if (nested) (result ??= {})[key] = nested;
                    continue;
                }
                if (condition(value)) (result ??= {})[key] = value;
            }
            
            return result;
        },
        object = { a: 1, b: { c: 2, d: -3, e: { f: { g: -4 } }, h: { i: 5, j: 6 } } },
        isPositive = n => n >= 0,
        result = deepFilter(object, isPositive);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }
    Login or Signup to reply.
  3. The problem you forget to collect the child objects containing filtered data. Check whether the child objects have data after filtering and them to the parent object.

    I’ve added K: 0 to the source object to have some falsy value to test with.

    Some fancy and fast no-braces solution:

    const walk = (obj, flt, out = null, add = null) => 
      typeof obj === 'number' ? flt(obj) ? obj : null: Object.keys(obj).forEach(key => (add = walk(obj[key], flt)) !== null && ((out ??= {})[key] = add)) || out;
    console.log(walk(object, n => n>=0 ));
    <script>
    const object = { a: 1, b: { c: 2, d: -3, e: { f: { g: -4 } }, h: { i: 5, j: 6 } }, k: 0};
    </script>

    More readable:

    const walk = (obj, flt) => {
      let out = null;
      if(typeof obj === 'number'){
        if(flt(obj)) return obj;
      }else{
        Object.keys(obj).forEach(key => {
          const add = walk(obj[key], flt);
          if(add !== null) (out ??= {})[key] = add;
        });
      }
      return out;
    }
    
    console.log(walk(object, n => n>=0 ));
    <script>
    const object = { a: 1, b: { c: 2, d: -3, e: { f: { g: -4 } }, h: { i: 5, j: 6 } }, k: 0};
    </script>
    ` Chrome/120
    ---------------------------------------------------------
    Alexander     1.00x  |  x1000000  165  165  169  175  180
    Nina Scholz   2.56x  |  x1000000  422  422  425  426  428
    ---------------------------------------------------------
    https://github.com/silentmantra/benchmark `
    
    const object = { a: 1, b: { c: 2, d: -3, e: { f: { g: -4 } }, h: { i: 5, j: 6 } }, k: 0 };
    
    // @benchmark Nina Scholz
    
    const deepFilter = (object, filter) => {
            const entries = [];
            
            for (const key in object) {
                const value = object[key];
                if (value && typeof value === "object") {
                    const sub = deepFilter(value, filter);
                    if (sub) entries.push([key, sub]);
                    continue;
                }
                if (filter(value)) entries.push([key, object[key]]);
            }
            
            return entries.length
                ? Object.fromEntries(entries)
                : undefined;
        };
        
        deepFilter(object, n => n>=0);
        
    // @benchmark Alexander
    
    const walk = (obj, flt, out = null, add = null) => 
      typeof obj === 'number' ? flt(obj) ? obj : null: Object.keys(obj).forEach(key => (add = walk(obj[key], flt)) !== null && ((out ??= {})[key] = add)) || out;
    
    walk(object, n => n>=0 );
    
    
    /*@end*/eval(atob('e2xldCBlPWRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3Rvcigic2NyaXB0Iik7aWYoIWUubWF0Y2hlcygiW2JlbmNobWFya10iKSl7bGV0IHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7dC5zcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9naC9zaWxlbnRtYW50cmEvYmVuY2htYXJrL2xvYWRlci5qcyIsdC5kZWZlcj0hMCxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHQpfX0='));
    Login or Signup to reply.
  4. function flattenObject(obj) {
      const result = {};
    
      function recurse(curr, prop) {
        if (Object(curr) !== curr) {
          if (curr >= 0) {
            result[prop] = curr;
          }
        } else if (Array.isArray(curr)) {
          for (let i = 0, l = curr.length; i < l; i++) {
            recurse(curr[i], prop + "[" + i + "]");
          }
          if (l == 0) {
            result[prop] = [];
          }
        } else {
          let isEmpty = true;
          for (let p in curr) {
            isEmpty = false;
            recurse(curr[p], prop ? prop + "." + p : p);
          }
          if (isEmpty && prop) {
            result[prop] = {};
          }
        }
      }
    
      recurse(obj, "");
      return result;
    }
    
    const obj = {
      a: 1,
      b: {
        c: 2,
        d: -3,
        e: {
          f: {
            g: -4,
          },
        },
        h: {
          i: 5,
          j: 6,
        },
      },
    };
    
    const flattenedObj = flattenObject(obj);
    
    // Filter the keys and remove negative values, using the last property name
    const output = {};
    for (const key in flattenedObj) {
      if (flattenedObj[key] >= 0) {
        const lastPropertyName = key.split('.').pop();
        output[lastPropertyName] = flattenedObj[key];
      }
    }
    
    console.log(output);
    Login or Signup to reply.
  5. The main issue is that you are reusing your resultObj, so nested objects are getting flattened.

    I’m also unsure why you’re using hasOwnProperty there. I feel like you’re building in a lot of unnecessary checks. All you need is something like this, where you go through each key recursively and add it to the result only if it maches the filter:

    function deepFilter(obj, filter) {
      let result = {};
      for (let key in obj) {
        if (typeof obj[key] === 'object') {
          let subObj = deepFilter(obj[key], filter);
          if (Object.keys(subObj).length) {
            result[key] = subObj;
          }
        } else if (filter(obj[key])) {
          result[key] = obj[key];
        }
      }
      return result;
    }
    
    // Input Object: 
    const obj = { a: 1, b: { c: 2, d: -3, e: { f: { g: -4, } }, h: { i: 5, j: 6 } } }
    
    // Callback Function:
    const filter = (n) => n >= 0
    
    // Output:
    // { a: 1, b: { c: 2, h: { i: 5, j: 6 } } }
    
    console.log(deepFilter(obj, filter));
    Login or Signup to reply.
  6. this is a classic interview question which can be easily solved using recursion.

    So basically you need to remove all negative numbers from the object key-value pairs.
    You need to write a RECURSIVE
    function that will take care of any level of nesting.
    Here is an example:

    const filterNegativeValuesFromObject = (inputObj, parentKey) => {
      let result = {};
      for (let key in inputObj) {
        const currentValue = inputObj[key];
        // if current value is an object, recursively call the function again to handle nesting and we also pass the current key as parent key, so that values can be stored in the current key to prevent flattening of the object
        if (typeof currentValue === "object") {
          const tempResult = filterNegativeValuesFromObject(inputObj[key], key);
          result = { ...result,
            ...tempResult
          };
        }
        // if current value is NOT an object, there are two cases
        else {
          // if there is a parent key
          if (parentKey !== "") {
            // current value of key is negative, remove it
            if (currentValue < 0) delete key;
            else {
              let temp = {};
              temp[key] = currentValue;
              result[parentKey] = { ...result[parentKey],
                ...temp
              };
            }
          }
          // if there is NO parent key
          else {
            // current value of key is negative, remove it
            if (currentValue < 0) delete key;
            else result[key] = currentValue;
          }
        }
      }
      return result;
    };
    
    
    console.log(filterNegativeValuesFromObject(inputObj, ""))
    <script>
      const inputObj = {
        a: 1,
        b: {
          c: 2,
          d: -3,
          e: {
            f: {
              g: -4,
            },
          },
          h: {
            i: 5,
            j: 6,
          },
        }
      };
    </script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search