sundowatch
sundowatch

Reputation: 3093

How to map according to key on objects?

I'm trying to map through object values and add text if the key is right on JavaScript. Here is our object:

{
    "id": "n27",
    "name": "Thomas More",
    "className": "level-1",
    "children": [
        {
            "id": "n1",
            "name": "Rousseau",
            "className": "level-2",
            "children": [
                {
                    "id": "n2",
                    "name": "Machiavelli",
                    "className": "level-3",
                    "children": [
                        {
                            "id": "n9",
                            "name": "Edison, Thomas",
                            "className": "level-4"
                        }
                    ]
                }
            ]
        },
        {
            "id": "n3",
            "name": "Einstein",
            "className": "level-2",
            "children": [
                {
                    "id": "n10",
                    "name": "Arf, Cahit",
                    "className": "level-3",
                    "children": [
                        {
                            "id": "n15",
                            "name": "Rawls, John",
                            "className": "level-4"
                        }
                    ]
                },
                {
                    "id": "n12",
                    "name": "Smith, Adam",
                    "className": "level-3",
                    "children": [
                        {
                            "id": "n11",
                            "name": "Kant, Immanuel",
                            "className": "level-4"
                        }
                    ]
                }
            ]
        },
        {
            "id": "n60",
            "name": "Turing, Alan",
            "className": "level-2"
        }
    ]
}

I want to add " YES" to their className's. So new object should look like this:

{
    "id": "n27",
    "name": "Thomas More",
    "className": "level-1 YES",
    "children": [
        {
            "id": "n1",
            "name": "Rousseau",
            "className": "level-2 YES",
            "children": [
                {
                    "id": "n2",
                    "name": "Machiavelli",
                    "className": "level-3 YES",
                    "children": [
                        {
                            "id": "n9",
                            "name": "Edison, Thomas",
                            "className": "level-4 YES"
                        }
                    ]
                }
            ]
        },
        {
            "id": "n3",
            "name": "Einstein",
            "className": "level-2 YES",
            "children": [
                {
                    "id": "n10",
                    "name": "Arf, Cahit",
                    "className": "level-3 YES",
                    "children": [
                        {
                            "id": "n15",
                            "name": "Rawls, John",
                            "className": "level-4 YES"
                        }
                    ]
                },
                {
                    "id": "n12",
                    "name": "Smith, Adam",
                    "className": "level-3 YES",
                    "children": [
                        {
                            "id": "n11",
                            "name": "Kant, Immanuel",
                            "className": "level-4 YES"
                        }
                    ]
                }
            ]
        },
        {
            "id": "n60",
            "name": "Turing, Alan",
            "className": "level-2 YES"
        }
    ]
}

I have tried this, but it adds to all of the keys:

const addToClassName = (datasource, fn) => {
    return Object.fromEntries(Object
        .entries(datasource, fn)
        .map(([k, v]) => [k, v && v.children != undefined && v.children.length > 0 ? addToClassName(v.children, fn) : fn(v)])
    );
}

let res = addToClassName(obj, v => v + ' YEP');

How can I do it?

Upvotes: 1

Views: 481

Answers (3)

A1exandr Belan
A1exandr Belan

Reputation: 4780

You can use lodash, if don't mind.

const _ = require('lodash');
const data = { "id": "n27", "name": "Thomas More", "className": "level-1", "children": [ { "id": "n1", "name": "Rousseau", "className": "level-2", "children": [ { "id": "n2", "name": "Machiavelli", "className": "level-3", "children": [ { "id": "n9", "name": "Edison, Thomas", "className": "level-4" } ] } ] }, { "id": "n3", "name": "Einstein", "className": "level-2", "children": [ { "id": "n10", "name": "Arf, Cahit", "className": "level-3", "children": [ { "id": "n15", "name": "Rawls, John", "className": "level-4" } ] }, { "id": "n12", "name": "Smith, Adam", "className": "level-3", "children": [ { "id": "n11", "name": "Kant, Immanuel", "className": "level-4" } ] } ] }, { "id": "n60", "name": "Turing, Alan", "className": "level-2" } ] };

const newData= _.cloneDeepWith(data, (val, key) => (
  (key === 'className') ? `${val} YES` : _.noop()
));

console.log(newData);
// {
//   id: 'n27',
//   name: 'Thomas More',
//   className: 'level-1 YES',
// ...
// }

Upvotes: 1

DecPK
DecPK

Reputation: 25398

If you can change the current obj object then you can achieve this using recursion as

function addClass(obj) {
  obj.className += " YES";
  obj.children && obj.children.forEach(addClass);
}

const obj = {
  id: "n27",
  name: "Thomas More",
  className: "level-1",
  children: [
    {
      id: "n1",
      name: "Rousseau",
      className: "level-2",
      children: [
        {
          id: "n2",
          name: "Machiavelli",
          className: "level-3",
          children: [
            {
              id: "n9",
              name: "Edison, Thomas",
              className: "level-4",
            },
          ],
        },
      ],
    },
    {
      id: "n3",
      name: "Einstein",
      className: "level-2",
      children: [
        {
          id: "n10",
          name: "Arf, Cahit",
          className: "level-3",
          children: [
            {
              id: "n15",
              name: "Rawls, John",
              className: "level-4",
            },
          ],
        },
        {
          id: "n12",
          name: "Smith, Adam",
          className: "level-3",
          children: [
            {
              id: "n11",
              name: "Kant, Immanuel",
              className: "level-4",
            },
          ],
        },
      ],
    },
    {
      id: "n60",
      name: "Turing, Alan",
      className: "level-2",
    },
  ],
};

function addClass(obj) {
  obj.className += " YES";
  obj.children && obj.children.forEach(addClass);
}

addClass(obj);
console.log(obj);

Upvotes: 1

Nick Parsons
Nick Parsons

Reputation: 50664

You don't need to use Object.fromEntries(), instead, you make your function return a new object, with a transformed className based on the return value of fn. You can then set the children: key on the object to be a mapped version of all the objects inside of the children array. When mapping, you can pass each child into the recursive call to your addToClassName() function. You can add the children key conditionally to the output object but checking if it exists (with children &&) and then by spreading the result using the spread syntax ...:

const data = { "id": "n27", "name": "Thomas More", "className": "level-1", "children": [ { "id": "n1", "name": "Rousseau", "className": "level-2", "children": [ { "id": "n2", "name": "Machiavelli", "className": "level-3", "children": [ { "id": "n9", "name": "Edison, Thomas", "className": "level-4" } ] } ] }, { "id": "n3", "name": "Einstein", "className": "level-2", "children": [ { "id": "n10", "name": "Arf, Cahit", "className": "level-3", "children": [ { "id": "n15", "name": "Rawls, John", "className": "level-4" } ] }, { "id": "n12", "name": "Smith, Adam", "className": "level-3", "children": [ { "id": "n11", "name": "Kant, Immanuel", "className": "level-4" } ] } ] }, { "id": "n60", "name": "Turing, Alan", "className": "level-2" } ] };
const addToClassName = (obj, fn) => ({
  ...obj, 
  className: fn(obj.className), 
  ...(obj.children && {children: obj.children.map(child => addToClassName(child, fn))})
});

console.log(addToClassName(data, v => v + " YES"));

Upvotes: 1

Related Questions