Get all keys in array of objects whose values are arrays

Viewed 320

I try to write all keys in an array of object whose values are arrays into a new array. Without duplicates. What is the best way in javascript, without libraries like lodash or underscore. I think that my solution is definitely improvable. The single objects and their keys are variable and not always identical. Suggestions welcome.

The intended output tom my example should be: [ "stuff", "type", "misc", "something" ]

const items = [{
    name: "Joe",
    occupied: "no",
    mobile: "yes",
    treatment: "no",
    date: "29-03-2020",
    age: "15",
    stuff: ["A", "B", "C"],
    type: ["1", "2"]
  },
  {
    name: "Jack",
    occupied: "yes",
    mobile: "no",
    treatment: "no",
    date: "02-03-2020",
    age: "20",
    stuff: ["A", "B", "C", "D", "E"],
    type: ["8", "6"],
    misc: ["otherStuff", "someStuff"]
  },
  {
    name: "Jane",
    occupied: "no",
    mobile: "yes",
    treatment: "yes",
    date: "15-02-2020",
    age: "28",
    stuff: ["C", "D", "E"],
    type: ["4", "7"],
    something: ["xxx", "ccc"]
  }
];


function getKeysWithArrayValues(myArray) {
  const result = [];
  myArray.forEach(item => Object.entries(item).forEach(itm => itm.filter(Array.isArray).forEach(x => result.push(itm.slice(0, 1)))));
  return flatArray(result)
};

function flatArray(array) {
  return array.reduce((acc, val) => Array.isArray(val) ? acc.concat(flatArray(val)) : acc.concat(val), []);
};
const ttt = getKeysWithArrayValues(items);
const flat = Array.from(new Set(ttt))
console.log(flat);

5 Answers

here is my solution, hope it helps. The algorithm is quite clear and I suppose there is no need to explan much. We just go through an array using reduce method and finally building the new array containing only keys that fit our requirements.

Array.isArray(rec[key]) checks if value is an array.

acc.indexOf(key) < 0 checks if the key has already been included into the resulting array on the one of previos steps.

const arr_ = items
  .reduce((acc, rec) => {
    return [...acc, ...Object.keys(rec).filter(key => Array.isArray(rec[key]) && acc.indexOf(key) < 0)]
  }, [])

Use flatMap to iterate through each object's keys and return a filtered array of keys, on the condition of whether the value at that key is an array:

const items=[{name:"Joe",occupied:"no",mobile:"yes",treatment:"no",date:"29-03-2020",age:"15",stuff:["A","B","C"],type:["1","2"]},{name:"Jack",occupied:"yes",mobile:"no",treatment:"no",date:"02-03-2020",age:"20",stuff:["A","B","C","D","E"],type:["8","6"],misc:["otherStuff","someStuff"]},{name:"Jane",occupied:"no",mobile:"yes",treatment:"yes",date:"15-02-2020",age:"28",stuff:["C","D","E"],type:["4","7"],something:["xxx","ccc"]}];

const keysWithArrays = new Set(
  items.flatMap(
    item => Object.keys(item).filter(key => Array.isArray(item[key]))
  )
);
console.log([...keysWithArrays]);

You could take a Set and get unique keys of the objects.

const
    items = [{ name: "Joe", occupied: "no", mobile: "yes", treatment: "no", date: "29-03-2020", age: "15", stuff: ["A", "B", "C"], type: ["1", "2"] }, { name: "Jack", occupied: "yes", mobile: "no", treatment: "no", date: "02-03-2020", age: "20", stuff: ["A", "B", "C", "D", "E"], type: ["8", "6"], misc: ["otherStuff", "someStuff"] }, { name: "Jane", occupied: "no", mobile: "yes", treatment: "yes", date: "15-02-2020", age: "28", stuff: ["C", "D", "E"], type: ["4", "7"], something: ["xxx", "ccc"] }],
    keys = Array.from(
         items.reduce(
             (s, o) => Object
                  .keys(o)
                  .reduce((t, k) => Array.isArray(o[k]) ? t.add(k) : t, s),
             new Set
        )
    );

console.log(keys);

You can use set and Array.isArray() to check if value is array.

let mySet = new Set();    const items = [{name: "Joe",occupied: "no",mobile: "yes",treatment: "no",date: "29-03-2020",age: "15",stuff: ["A", "B", "C"],type: ["1", "2"]},{name: "Jack",occupied: "yes",mobile: "no",treatment: "no",date: "02-03-2020",age: "20",stuff: ["A", "B", "C", "D", "E"],type: ["8", "6"],misc: ["otherStuff", "someStuff"]
}, {name: "Jane",occupied: "no",mobile: "yes",treatment: "yes",date: "15-02-2020",age: "28",stuff: ["C", "D", "E"],type: ["4", "7"],something: ["xxx", "ccc"]}];

items.forEach((obj)=> Object.keys(obj).forEach( prop=>{if(Array.isArray(obj[prop])) 
mySet.add(prop)})); console.log(Array.from(mySet));
.as-console-wrapper { max-height: 100% !important; top: 0; }

One more way with Object.fromEntries, flatMap and filter.

const items = [
  {
    name: "Joe",
    occupied: "no",
    mobile: "yes",
    treatment: "no",
    date: "29-03-2020",
    age: "15",
    stuff: ["A", "B", "C"],
    type: ["1", "2"]
  },
  {
    name: "Jack",
    occupied: "yes",
    mobile: "no",
    treatment: "no",
    date: "02-03-2020",
    age: "20",
    stuff: ["A", "B", "C", "D", "E"],
    type: ["8", "6"],
    misc: ["otherStuff", "someStuff"]
  },
  {
    name: "Jane",
    occupied: "no",
    mobile: "yes",
    treatment: "yes",
    date: "15-02-2020",
    age: "28",
    stuff: ["C", "D", "E"],
    type: ["4", "7"],
    something: ["xxx", "ccc"]
  }
];

const keys = Object.keys(
  Object.fromEntries(
    items.flatMap(item =>
      Object.entries(item).filter(([, value]) => Array.isArray(value))
    )
  )
);

console.log(keys);

Related