Home > Mobile >  JavaScript Return from Nested Array WITHOUT specified Keys
JavaScript Return from Nested Array WITHOUT specified Keys

Time:12-07

I'm trying to write a function that will return an object omitting key(s) from a possibly nested items in an array. I found a close answer on a different question, but not quite what I was looking for. Any feedback would be greatly appreciated! Here is the code I'm tinkering with right now;

function omit(obj, keys) {
    let newObj = [];
    for (let i of obj) {
        if (i === keys) {
            //do nothing
        } else {
            //newObj.push[i]; nope?
            return newObj;
        }
        //return newObj;
    }
}

EDIT: Here is the formula and some example input/output;

var x = {
    key1: true,
    key2: 2,
    key3: {
        nested1: 'JavaScript'
    }
};

omit(x, ['key2', 'key3']) ==> {
    key1: true
}

omit(x, ['nested1']) ==> {
    key1: true,
    key2: 2,
    key3: ''
}

CodePudding user response:

So, my interpretation of your question is that you want to scan an array for values and return an array without them, but the array you're scanning could possibly contain arrays that we want to check as well. We don't know the format of the arrays and how deep it goes. How can we solve this problem? The answer is recursion. First, we need to understand how to return an array that doesn't have certain keys. So let's start with that:

function omitFromArray(array, valuesToOmit) {
  const newArray = [] // we never reassign, use const
  for (const value of array) { // use const here as well, technically we make a new variable each iteration of the loop
    if (valuesToOmit.indexOf(value) === -1) { // if something doesn't exist in an array when we test for an index, it'll return -1
      newArray.push(value)
    }
  }
  return newArray // we want return to exist *outside* of the for loop so the for loop has a chance to run through all the required iterations;
                  // in your example, the return keyword is inside the for loop causing it to always give up after the first iteration
}

const arrayToTest = [1, 2, 3, 4, 5, 6]
const testValuesToOmit = [1, 4, 6]

console.log(omitFromArray(arrayToTest, testValuesToOmit))
// returns [2, 3, 5]

This works just great, but the problem is that it's shallow; it'll only scan the values in the first level of the array. To solve this, we need a recursive function. Here's what it may look like:

function omitFromNestedArray(array, valuesToOmit) {
  function walk(array, valuesToOmit) { // this is a modification of the function we wrote before
    const newArray = []
    for (const value of array) {
      if (Array.isArray(value)) { // except now, we check if the current value happens to be another array
        newArray.push(walk(value, valuesToOmit)) // if it is, we actually call the function *inside itself* and return *its* value into our new array
      } else {
        if (valuesToOmit.indexOf(value) === -1) { // if it's just a value, we can check it like normal
          newArray.push(value) // and put it in our array
        }
      }
    }
    return newArray // and give it back at the very end
  }
  return walk(array, valuesToOmit) // we finally need to call the function at the top level of our array and return that value
}

const nestedArrayToTest = [1, 2, [3, [4, 5], 6], 7]
const testValuesToOmit = [1, 4, 6]

console.log(omitFromNestedArray(nestedArrayToTest, testValuesToOmit))
// returns [2, [3, [5]], 7]

So, the basic concept of a recursive function is that the function calls itself. The base 'walk' function does what we normally to omit values, but now it checks if we're up against another array; if we are, let's pause and go into that one first and go through it. We keep doing that until we get to the lowest level, and then the function naturally starts going back out and out to finally give us a new array. If you have any questions, please ask!

EDIT: To adapt the code to work with objects instead of arrays, we do this:

function removeUnwantedKeysFromObject(obj, unwantedKeys) {
  function walk(obj, unwantedKeys) {
    for (const key of Object.keys(obj)) { // iterating through Object.keys(obj) will return each key in the object
      const value = obj[key] // we have to create a variable for the value this way
      if (typeof value === 'object') { // this is how we check if that value is another object
        walk(value, unwantedKeys)
      } // we can't use else because we also want to delete this whole object if it's inside the keys we want to remove
      if (unwantedKeys.indexOf(key) !== -1) { // if it's just a value, we can check it like normal
        delete obj[key] // this is how you remove a key from an object
      }
    }
  }
  walk(obj, unwantedKeys)
}

let objectToTest = {key1: true, key2: 2, key3: { nested1: 'JavaScript' }};
removeUnwantedKeysFromObject(objectToTest, ['key2', 'key3'])
console.log(objectToTest)
// returns { key1: true }
objectToTest = {key1: true, key2: 2, key3: { nested1: 'JavaScript' }};
removeUnwantedKeysFromObject(objectToTest, ['nested1'])
console.log(objectToTest)
// returns { key1: true, key2: 2, key3: {} }

I do have a slight warning here: this code edits the original object. If you want it to construct a new object, you'll have to edit the code to construct a new object on-the-fly or use a library (such as lodash) to deep clone the original object and return the new one. Otherwise, this is the same method but works with objects instead of arrays.

CodePudding user response:

The omit() function defined in the demo below gets the keys of the object passed and filters out the ones to be excluded then rebuilds the object based on the keys left.

Using recursion omit() is run on all object values of these remaining keys.

const x = {
    key1: true,
    key2: 2,
    key3: {
        nested1: 'JavaScript'
    }
};

const omit = ( obj, keys ) => obj.constructor.name === 'Object' && obj != null ? Object.keys(obj).filter(k => !keys.includes(k)).reduce((a, c) => ({
    ...a,
    [c]: omit( obj[c], keys )
}), {}) : obj;

console.log( omit(x, ['key2']) );

console.log( omit(x, ['nested1']) );

console.log( omit(x, ['nested1','key1']) );

console.log( omit(x, ['key3']) );
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related