0

I'm working with this array of nested objects, getted from an API:

const myObj = [
    {
        "$id":"1",
        "Description":"WA State",
        "Place":"WA",
        "Data":[
        {
            "$id":"2",
            "Description":"Years",
            "Indicators":[
            {
                "$id":"3",
                "Year":2017,
                "Points":22191,
                "Goal":"28000",
                "Description":"Year 2017"
            },
            {
                "$id":"4",
                "Year":2018,
                "Points":25994,
                "Goal":"28000",
                "Description":"Year 2018"
            }
            ]
        },
        {
            "$id":"5",
            "Description":"Local Goal",
            "Indicators":[
            {
                "$id":"6",
                "Year":2018,
                "Points":25994,
                "Goal":"28000",
                "Description":"Year 2018"
            }
            ]
        },
        {
            "$id":"7",
            "Description":"Remote Goal",
            "Indicators":[
            {
                "$id":"8",
                "Year":2018,
                "Points":55857,
                "Goal":"84000",
                "Description":"Year 2018"
            }
            ]
        }
        ]
    },

    {
        "$id":"9",
        "Description":"NY State",
        "Place":"NY",
        "Data":[
        {
            "$id":"10",
            "Description":"Years",
            "Indicators":[
            {
                "$id":"11",
                "Year":2017,
                "Points":23451,
                "Goal":"27000",
                "Description":"Year 2017"
            },
            {
                "$id":"12",
                "Year":2018,
                "Points":21953,
                "Goal":"26000",
                "Description":"Year 2018"
            }
            ]
        },
        {
            "$id":"13",
            "Description":"Local Goal",
            "Indicators":[
            {
                "$id":"14",
                "Year":2018,
                "Points":24195,
                "Goal":"25000",
                "Description":"Year 2018"
            }
            ]
        },
        {
            "$id":"15",
            "Description":"Remote Goal",
            "Indicators":[
            {
                "$id":"16",
                "Year":2018,
                "Points":80857,
                "Goal":"90000",
                "Description":"Year 2018"
            }
            ]
        }
        ]
    }
];

I need to delete all the $id and Description properties from the objects, but not mutating the object. I'm trying to do it using .reduce():

const props = ['$id', 'Descripcion'];

function removeKeys(obj, prop){
  return props.map( (prop, index) => Object.keys(obj).reduce((object, key) => {
    if (key !== prop[index]) {
      object[key] = obj[key]
    }
    if(object.hasOwnProperty(key))
      removeKeys(obj, prop[index])
    return object
  }, {})
  )
}

console.log( removeKeys(myObj, props) );

// RangeError: Maximum call stack size exceeded

And does not works. Any ideas on how I can achieve this using .reduce()

PD: My question is not a duplicate of, because I'm mentioning and specifying the use of reduce to achieve the goal. In the other question, the answers are about using the "for...loop" syntax.

robe007
  • 3,523
  • 4
  • 33
  • 59
  • Why does it need to be done using `reduce`? – Dexygen Oct 18 '18 at 21:41
  • Because I don't want to mutate the state, and also I'm looking for an elegant way to do it. – robe007 Oct 18 '18 at 22:03
  • First, don't worry about elegant, instead worry about something that works. Sounds like you want a separate, new array (of objects)? – Dexygen Oct 18 '18 at 22:09
  • Yes, but ... it is something difficult to do it with `reduce` ? – robe007 Oct 18 '18 at 22:11
  • recursion definitely seems to be the way to go, not sure how reduce fits in – Dexygen Oct 18 '18 at 22:16
  • Possible duplicate of [Using JavaScript what's the quickest way to recursively remove properties and values from an object?](https://stackoverflow.com/questions/31728988/using-javascript-whats-the-quickest-way-to-recursively-remove-properties-and-va) – Dexygen Oct 18 '18 at 22:17
  • I've found what seems to be an identical question. If you don't want to modify the original array, you can use something like `const myObjCopy = JSON.parse(JSON.stringify(myObj))` to create a clone/copy that doesn't refer to the original – Dexygen Oct 18 '18 at 22:20
  • 1
    What is the reason someone downvoted my answer? I have proposed the solution with the approach the author of the question had chosen – Volodymyr Oct 18 '18 at 22:24
  • @GeorgeJempty It is NOT. If you see the answer below, you can see that it is possible. And ... In the other question, the answers are about using the "for...loop" syntax. – robe007 Oct 18 '18 at 22:29

1 Answers1

1

You forgot to check if property is an object or an array of objects. Also you'he been shallowing prop argument of removeKeys with prop argument of your mapping function. Basically this code will do the trick:

const props = ['$id', 'Description'];

function removeKeys(obj){
  return Object.keys(obj).reduce((object, key) => {
     if (Array.isArray(obj[key])) {
       object[key] = obj[key].map(item => removeKeys(item));
     }
     
     else if (typeof obj[key] === 'object') {
       console.log(object);
       object[key] = removeKeys(obj[key]);
     }
     
     else if (props.indexOf(key) === -1) {
       object[key] = obj[key];
     }
     
     return object;
  }, {});
}

console.log( removeKeys(myObj) );
<script>
  myObj = 
    {
        "$id":"1",
        "Description":"WA State",
        "Place":"WA",
        "Data":[
        {
            "$id":"2",
            "Description":"Years",
            "Indicators":[
            {
                "$id":"3",
                "Year":2017,
                "Points":22191,
                "Goal":"28000",
                "Description":"Year 2017"
            },
            {
                "$id":"4",
                "Year":2018,
                "Points":25994,
                "Goal":"28000",
                "Description":"Year 2018"
            }
            ]
        },
        {
            "$id":"5",
            "Description":"Local Goal",
            "Indicators":[
            {
                "$id":"6",
                "Year":2018,
                "Points":25994,
                "Goal":"28000",
                "Description":"Year 2018"
            }
            ]
        },
        {
            "$id":"7",
            "Description":"Remote Goal",
            "Indicators":[
            {
                "$id":"8",
                "Year":2018,
                "Points":55857,
                "Goal":"84000",
                "Description":"Year 2018"
            }
            ]
        }
        ]
    };
</script>
Volodymyr
  • 1,360
  • 1
  • 7
  • 12
  • 1
    What is the reason someone downvoted my post? I have proposed the solution with the approach the author of the question had chosen – Volodymyr Oct 18 '18 at 22:22
  • Thanks for your answer Volodymyr. I have upvoted your answer. But also, I have read that it is possible using `spread operator` inside `reduce`, as you can see [here](https://blog.ricardofilipe.com/post/immutable-changes-in-js) on the topic *"Deleting properties"*. Do you know also about that ? – robe007 Oct 18 '18 at 22:37
  • 1
    Yes, you can go with spread operator (`Object.assign` also can do the trick) as well. However you will need to iterate over properties and check if they are objects or not. Why? Because spread operator does not clones deeply. So if you will clone your original object with spread operator and then change something inside an object nested inside you will also mutate the original object. – Volodymyr Oct 18 '18 at 22:49
  • 1
    You can also mark this answer as the right one if it solves your issue – Volodymyr Oct 18 '18 at 22:52
  • Can you please, post an answer too using `Object.assign` or `spread operators`? It's for the sake of understand. – robe007 Oct 18 '18 at 22:52