Getting all key value pairs from object into a single array

Viewed 109

I have an object as per below:

{
      items: [
        {
          id: '6978119a-481e-4ff5-bd02-2b51d2aed804',
          name: 'Car',
          items: [],
        },
        {
          id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8',
          name: 'Toolbox',
          items: [
            {
              id: '1764e295-4ee7-4455-80b4-ea011e111785',
              name: 'Spanner',
              items: [],
            },
            {
              id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406',
              name: 'Hammer',
              items: [],
            },
          ],
        },
        {
          id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95',
          name: 'Shed',
          items: [
            {
              id: '0a81991c-51e7-4cea-b1cf-a63ea7200744',
              name: 'Axe',
              items: [],
            },
          ],
        },
      ],
      };

I am trying to figure out two things:

  1. Get an array of objects, where the key is 'name' (e.g. below)
var array = [{name: 'Car'}, {name: 'Toolbox'}, {name: 'Spanner'}, {name: 'Hammer'}, {name: 'Shed'}, {name: 'Axe'}]
  1. Get an array of objects, where the key is 'name' but has no items below it (essentially childless) (e.g. below)
var array = [{name: 'Car'}, {name: 'Spanner'}, {name: 'Hammer'}, {name: 'Axe'}]

I attempted putting something together that iterates to a list and pushes to an array but after the 3rd nested loop, I knew I was doing something wrong.

I've also explored Object.entries but it only gets the top layer.

6 Answers

You can use recursion to check all items and child items

const data = {
      items: [
        {
          id: '6978119a-481e-4ff5-bd02-2b51d2aed804',
          name: 'Car',
          items: [],
        },
        {
          id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8',
          name: 'Toolbox',
          items: [
            {
              id: '1764e295-4ee7-4455-80b4-ea011e111785',
              name: 'Spanner',
              items: [],
            },
            {
              id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406',
              name: 'Hammer',
              items: [],
            },
          ],
        },
        {
          id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95',
          name: 'Shed',
          items: [
            {
              id: '0a81991c-51e7-4cea-b1cf-a63ea7200744',
              name: 'Axe',
              items: [],
            },
          ],
        },
      ],
      };

const getNames = (items, result = []) => {
   
   for(const item of items) {
      if(!item.items || !item.items.length) {
         // no sub-items, just push name to the result
         result.push({ name: item.name })
      } else {
         //loop through all child items to find child names
         result = getNames(item.items, result)
      }
   }
   return result
}

const finalResult = getNames(data.items)
console.log(finalResult)

Another version with concat (no result param) in recursion

const data = {
      items: [
        {
          id: '6978119a-481e-4ff5-bd02-2b51d2aed804',
          name: 'Car',
          items: [],
        },
        {
          id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8',
          name: 'Toolbox',
          items: [
            {
              id: '1764e295-4ee7-4455-80b4-ea011e111785',
              name: 'Spanner',
              items: [],
            },
            {
              id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406',
              name: 'Hammer',
              items: [],
            },
          ],
        },
        {
          id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95',
          name: 'Shed',
          items: [
            {
              id: '0a81991c-51e7-4cea-b1cf-a63ea7200744',
              name: 'Axe',
              items: [],
            },
          ],
        },
      ],
      };

const getNames = (items) => {
   let result = []
   for(const item of items) {
      if(!item.items || !item.items.length) {
         result.push({ name: item.name })
      } else {
         result = result.concat(getNames(item.items))
      }
   }
   return result
}

const finalResult = getNames(data.items)
console.log(finalResult)

 const data = {
  items: [
    {
      id: '6978119a-481e-4ff5-bd02-2b51d2aed804',
      name: 'Car',
      items: [],
    },
    {
      id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8',
      name: 'Toolbox',
      items: [
        {
          id: '1764e295-4ee7-4455-80b4-ea011e111785',
          name: 'Spanner',
          items: [],
        },
        {
          id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406',
          name: 'Hammer',
          items: [],
        },
      ],
    },
    {
      id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95',
      name: 'Shed',
      items: [
        {
          id: '0a81991c-51e7-4cea-b1cf-a63ea7200744',
          name: 'Axe',
          items: [],
        },
      ],
    },
  ],
  };
var array = [];
var name;
data.items.forEach(item=>{
array.push({'name':item.name}); 
item.items.length > 0 ? item.items.forEach(nestedItem=>
{array.push({'name':nestedItem.name})}) : null
});

console.log('Resulted Array',array);

Output: var array = [{name: 'Car'}, {name: 'Toolbox'}, {name: 'Spanner'}, {name: 'Hammer'}, {name: 'Shed'}, {name: 'Axe'}]

Note: if you are using older version of js, instead of forEach loop, you can use old traditional for loop.

To Get all names in the Array :

    const item = {
  items: [
    {
      id: "6978119a-481e-4ff5-bd02-2b51d2aed804",
      name: "Car",
      items: []
    },
    {
      id: "9cc8fe29-1106-4f3e-958d-250ca74cc1e8",
      name: "Toolbox",
      items: [
        {
          id: "1764e295-4ee7-4455-80b4-ea011e111785",
          name: "Spanner",
          items: []
        },
        {
          id: "3a2c1c78-9c97-457e-bfdd-ea13ced94406",
          name: "Hammer",
          items: []
        }
      ]
    },
    {
      id: "aa41d735-ebb7-48dd-a5b0-dc6806c4ee95",
      name: "Shed",
      items: [
        {
          id: "0a81991c-51e7-4cea-b1cf-a63ea7200744",
          name: "Axe",
          items: []
        }
      ]
    }
  ]
};
    const firstArray = item.items.map((data) => {
        if (data.items.length) {
          const arrayName = data.items.map((dataa) => dataa.name);
          return [...arrayName, data.name];
        }
        return { name: data.name };
      });
      const getArrays = firstArray.flat(Infinity);
      const result = getArrays.map((data) => {
        if (typeof data === 'string') {
          return { name: data };
        }
        return data;
      });
      console.log(result);

I would define a generator for this. Here are two versions. One for getting all item names:

let data = {items: [{id: '6978119a-481e-4ff5-bd02-2b51d2aed804',name: 'Car',items: [],},{id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8',name: 'Toolbox',items: [{id: '1764e295-4ee7-4455-80b4-ea011e111785',name: 'Spanner',items: [],},{id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406',name: 'Hammer',items: [],},],},{id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95',name: 'Shed',items: [{id: '0a81991c-51e7-4cea-b1cf-a63ea7200744',name: 'Axe',items: [],},],},],};

function* iterAll({name, items}) {
    if (name !== undefined) yield { name };
    for (let item of items) yield* iterAll(item);
}

let result = [...iterAll(data)];
console.log(result);

...and one that only gets the "leaves" of the hierarchy:

let data = {items: [{id: '6978119a-481e-4ff5-bd02-2b51d2aed804',name: 'Car',items: [],},{id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8',name: 'Toolbox',items: [{id: '1764e295-4ee7-4455-80b4-ea011e111785',name: 'Spanner',items: [],},{id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406',name: 'Hammer',items: [],},],},{id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95',name: 'Shed',items: [{id: '0a81991c-51e7-4cea-b1cf-a63ea7200744',name: 'Axe',items: [],},],},],};

function* iterLeaves({name, items}) {
    if (name !== undefined && !items.length) yield { name };
    for (let item of items) yield* iterLeaves(item);
}

let result = [...iterLeaves(data)];
console.log(result);

you can easily solve that with recursion:

let data={items:[{id:"6978119a-481e-4ff5-bd02-2b51d2aed804",name:"Car",items:[]},{id:"9cc8fe29-1106-4f3e-958d-250ca74cc1e8",name:"Toolbox",items:[{id:"1764e295-4ee7-4455-80b4-ea011e111785",name:"Spanner",items:[]},{id:"3a2c1c78-9c97-457e-bfdd-ea13ced94406",name:"Hammer",items:[]}]},{id:"aa41d735-ebb7-48dd-a5b0-dc6806c4ee95",name:"Shed",items:[{id:"0a81991c-51e7-4cea-b1cf-a63ea7200744",name:"Axe",items:[]}]}]};

function extractNames(items,allNames){

    let result = items.flatMap(e =>{
        if(e.items.length === 0)
            return e.name
        else{
            if(allNames){
                return [e.name,extractNames(e.items,allNames)].flat(4)
            }
            else{
                return extractNames(e.items,allNames)
            }
        }
    })

    return result

}

let first_result = extractNames(data.items,true)
console.log(first_result)

let second_result = extractNames(data.items,false)
console.log(second_result)

You could build two functions using .flatMap(), the idea for this first function is to recursively return and map your inner items arrays, and then concatenate them all together (which is what .flatMap() does for you). So for each object you encounter, you can return an array with a name object along with the names of its children within items if the items array has values within it:

const data = { items: [{ id: '6978119a-481e-4ff5-bd02-2b51d2aed804', name: 'Car', items: [], }, { id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8', name: 'Toolbox', items: [{ id: '1764e295-4ee7-4455-80b4-ea011e111785', name: 'Spanner', items: [], }, { id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406', name: 'Hammer', items: [], }, ], }, { id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95', name: 'Shed', items: [{ id: '0a81991c-51e7-4cea-b1cf-a63ea7200744', name: 'Axe', items: [], }, ], }, ], };

const getAllNames = (items) => items.flatMap(obj =>  [
  {name: obj.name}, // always add your {name} object
  ...(obj.items.length ? getAllNames(obj.items) : []) // only add recursive {name} children if current object has items
]);

const allNames = getAllNames(data.items);
console.log(allNames); // [{name: 'Car'}, {name: 'Toolbox'}, {name: 'Spanner'}, {name: 'Hammer'}, {name: 'Shed'}, {name: 'Axe'}]

Applying similar logic to the above, you can only grab those objects that don't have children by checking if obj.items.length is falsy. If it is, then it doesn't have children, and so you can return an object with the current name in it. If it does have children, then you can recurse down those to find any children that are leaves (ie: have an empty items array):

const data = { items: [{ id: '6978119a-481e-4ff5-bd02-2b51d2aed804', name: 'Car', items: [], }, { id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8', name: 'Toolbox', items: [{ id: '1764e295-4ee7-4455-80b4-ea011e111785', name: 'Spanner', items: [], }, { id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406', name: 'Hammer', items: [], }, ], }, { id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95', name: 'Shed', items: [{ id: '0a81991c-51e7-4cea-b1cf-a63ea7200744', name: 'Axe', items: [], }, ], }, ], };

const getLeafNames = (items) => items.flatMap(obj => obj.items.length
  ? getLeafNames(obj.items)
  : {name: obj.name}
);

const leafNames = getLeafNames(data.items);
console.log(leafNames); // [{name: 'Car'}, {name: 'Spanner'}, {name: 'Hammer'}, {name: 'Axe'}]

You could combine both of these into one, and specify a mapping function as an argument that determines what to add to your array. The array you return from this mapping function is merged into the final array, which means you can return multiple objects, or return an empty array if you wish to add nothing:

const data = { items: [{ id: '6978119a-481e-4ff5-bd02-2b51d2aed804', name: 'Car', items: [], }, { id: '9cc8fe29-1106-4f3e-958d-250ca74cc1e8', name: 'Toolbox', items: [{ id: '1764e295-4ee7-4455-80b4-ea011e111785', name: 'Spanner', items: [], }, { id: '3a2c1c78-9c97-457e-bfdd-ea13ced94406', name: 'Hammer', items: [], }, ], }, { id: 'aa41d735-ebb7-48dd-a5b0-dc6806c4ee95', name: 'Shed', items: [{ id: '0a81991c-51e7-4cea-b1cf-a63ea7200744', name: 'Axe', items: [], }, ], }, ], };

const getNodes = (items, mapper) => {
  return items.flatMap(obj => [
    ...mapper(obj), 
    ...(obj.items.length ? getNodes(obj.items, mapper) : [])
  ]);
}

const all = getNodes(data.items, ({name}) => [{name}]); // include all items, map each to `name`
const leaves = getNodes(data.items, obj => !obj.items.length ? [{name: obj.name}] : []); // include if it doesn't have children
console.log(all); // [{name: 'Car'}, {name: 'Toolbox'}, {name: 'Spanner'}, {name: 'Hammer'}, {name: 'Shed'}, {name: 'Axe'}]
console.log(leaves); // [{name: 'Car'}, {name: 'Spanner'}, {name: 'Hammer'}, {name: 'Axe'}]

Related