Get only some keys of object of objects

Viewed 574

I've this object:

const dataset = {
  "2019": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4
  },
  "2020": {
    "a": 2,
    "b": 4,
    "c": 6,
    "d": 8
  },
  "2021": {
    "a": 10,
    "b": 11,
    "c": 12,
    "d": 13
  }
}

I would like to obtain these two objects:

const obj1 = {
  "2019": {
    "a": 1,
    "c": 3,
  },
  "2020": {
    "a": 2,
    "c": 6,
  },
  "2021": {
    "a": 10,
    "c": 12,
  }
}

const obj2 = {
  "2019": {
    "b": 2,
    "d": 4
  },
  "2020": {
    "b": 4,
    "d": 8
  },
  "2021": {
    "b": 11,
    "d": 13
  }
}

So "split" the object in two objects based on some keys of the inner objects. Here is what I tried:

function pickKeys(dataObj, keys) {
  return Object.entries(dataObj).map(([d, obj]) => {
  return { [d]: _.pick(obj, keys) }
})
}
const obj1 = pickKeys(dataset, ['a', 'c'])

The result is:

const obj1 = [
  { '2019': { a: 1, c: 3 } },
  { '2020': { a: 2, c: 6 } },
  { '2021': { a: 10, c: 12 } }
]

So almost there but it's not perfect. Which is the better way to do that?

4 Answers

You do this using combination of map, reduce methods and one for...in loop that will turn array of keys into array of objects. Then you can use array destructuring to get two separate objects.

const dataset = {"2019":{"a":1,"b":2,"c":3,"d":4},"2020":{"a":2,"b":4,"c":6,"d":8},"2021":{"a":10,"b":11,"c":12,"d":13}}

const [a, b] = [['a', 'c'], ['b', 'd']]
.map(keys => keys.reduce((r, key) => {
  for (let year in dataset) {
    if (!r[year]) r[year] = {}
    r[year][key] = dataset[year][key]
  }

  return r;
}, {}))

console.log(a)
console.log(b)

The problem is that map returns an array with replaced elements, while you want an object.

Since you are already using Lodash you could use mapValues to transform the values of an object and return an object instead of an array.

function pickKeys(dataObj, keys) {
  return _.mapValues(dataObj, obj => _.pick(obj, keys));
}

function pickKeys(dataObj, keys) {
  return _.mapValues(dataObj, obj => _.pick(obj, keys));
}

const dataset = {
  "2019": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4
  },
  "2020": {
    "a": 2,
    "b": 4,
    "c": 6,
    "d": 8
  },
  "2021": {
    "a": 10,
    "b": 11,
    "c": 12,
    "d": 13
  }
}

console.log(pickKeys(dataset, ["a", "c"]));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

You could map the wanted keys by using the entries of the given object.

const
    dataset = { 2019: { a: 1, b: 2, c: 3, d: 4 }, 2020: { a: 2, b: 4, c: 6, d: 8 }, 2021: { a: 10, b: 11, c: 12, d: 13 } },
    groups = [['a', 'c'], ['b', 'd']],
    [result1, result2] = Object
        .entries(dataset)
        .reduce((r, [k, o]) =>
            groups.map((group, i) =>
                group.reduce(
                    (q, g) => ({ ...q, [k]: { ...q[k], [g]: o[g] } }),
                    r[i] || {}
                )
            ),
            []
        );

console.log(result1);
console.log(result2);
.as-console-wrapper { max-height: 100% !important; top: 0; }

The next provided example code is reduce based, generic but configurable in its usage ...

function createAndCollectSubdata(collector, dataEntry) {

  const { keyLists, subdataList } = collector;
  const [ dataKey, dataValue ] = dataEntry;

  keyLists.forEach((keyList, idx) => {

    const data = subdataList[idx] || (subdataList[idx] = {});
    const subdata = data[dataKey] || (data[dataKey] = {}) ;

    keyList.forEach(key => subdata[key] = dataValue[key]);
  });
  return collector;
}

const dataset = {
  "2019": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4
  },
  "2020": {
    "a": 2,
    "b": 4,
    "c": 6,
    "d": 8
  },
  "2021": {
    "a": 10,
    "b": 11,
    "c": 12,
    "d": 13
  }
};
const [

  acSubdata,
  bdSubdata
  
] = Object.entries(dataset).reduce(createAndCollectSubdata, {

  keyLists: [["a", "c"], ["b", "d"]],
  subdataList: []

}).subdataList;

console.log('acSubdata :', acSubdata);
console.log('bdSubdata :', bdSubdata);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Related