0

Let's say I have an object with the structure

data = {
    a : [{values: {key1: 5, key2: "abc"}}, {values: {key1: 3, key2: "abc"}}, {values: {key1: 4, key2: "cde"}}],
    b : [{values: {key1: 3, key2: "ffe"}}, {values: {key1: 11, key2: "gga"}}, {values: {key1: 7, key2: "abc"}}]
}

I want to extract elements where key2 == "abc".

Expected output:

data = {
    a : [{values: {key1: 5, key2: "abc"}}, {values: {key1: 3, key2: "abc"}}],
    b : [{values: {key1: 7, key2: "abc"}}]
}

I've tried to follow similar examples but failed to implement what I wanted.

Phil
  • 157,677
  • 23
  • 242
  • 245
ksaliya
  • 182
  • 7

5 Answers5

8

Use Object.entries() to extract all the key / value pairs of data as an array, map over each pair and then filter on each value to extract the ones you want.

You can then join it back up using Object.fromEntries()

const data = {
    a : [{values: {key1: 5, key2: "abc"}}, {values: {key1: 3, key2: "abc"}}, {values: {key1: 4, key2: "cde"}}],
    b : [{values: {key1: 3, key2: "ffe"}}, {values: {key1: 11, key2: "gga"}}, {values: {key1: 7, key2: "abc"}}]
}

const findKey = 'key2'
const findValue = 'abc'

const newData = Object.fromEntries(Object.entries(data).map(([ key, val ]) =>
  [ key, val.filter(({ values }) => values?.[findKey] === findValue) ]))
  
console.log(newData)

In case you haven't seen Optional Chaining yet, this...

values?.[findKey] === findValue

is equivalent to

!!values && values[findKey] === findValue
Phil
  • 157,677
  • 23
  • 242
  • 245
  • 1
    My brain hurts trying to follow all the destructuring going on there. – Adrian Brand Jul 01 '20 at 04:56
  • While this works there is so much destructuring here that you might loose the flow – Manos Kounelakis Jul 01 '20 at 04:59
  • @ManosKounelakis there are only two destructures here, one array (`[ key, val ]`) and one object (`{ value }`). Not sure what you consider too much? – Phil Jul 01 '20 at 04:59
  • do you actually need the optional chaining? i thought if you try to access a property at the first layer that is not there it returns undefined? – bill.gates Jul 01 '20 at 05:06
  • @Ifaruki it's there in case any of the array entries doesn't have a `values` property. If they did, `values` would be `undefined` and `values[findKey]` would throw an error – Phil Jul 01 '20 at 05:07
  • At first glance I saw the return value of the arrow callback inside map and thought it was a destructure. – Manos Kounelakis Jul 01 '20 at 05:11
  • i know i was just thinking about that because they OP didnt say that it could happen that there is no `values` available. – bill.gates Jul 01 '20 at 05:16
4

Simple for of loop that iterates over keys and filters the arrays and reassigns properties directly.
Mutates data directly.

Uses filter and destructures key2 to filter on it.

const data = {
    a : [{values: {key1: 5, key2: "abc"}}, {values: {key1: 3, key2: "abc"}}, {values: {key1: 4, key2: "cde"}}],
    b : [{values: {key1: 3, key2: "ffe"}}, {values: {key1: 11, key2: "gga"}}, {values: {key1: 7, key2: "abc"}}]
}

for(const k of Object.keys(data))
  data[k] = data[k].filter(({values: {key2}})=>key2==='abc')
  
console.log(data)

Added check of .length to prune keys with empty arrays,
and set default value for key2 so values property is optional:

const data = {
    a : [{values: {key1: 5, key2: "abc"}}, {values: {key1: 3, key2: "abc"}}, {values: {key1: 4, key2: "cde"}}],
    b : [{values: {key1: 3, key2: "ffe"}}, {values: {key1: 11, key2: "gga"}}, {values: {key1: 7, key2: "abc"}}]
}

for(const k of Object.keys(data))
  (data[k] = data[k].filter(({values:{key2}={}})=>key2==='abc'))
    .length || delete data[k]
  
console.log(data)
user120242
  • 14,918
  • 3
  • 38
  • 52
1

You can reduce the properties of the object to the ones that pass a filter

const data = {
    a : [{values: {key1: 5, key2: "abc"}}, {values: {key1: 3, key2: "abc"}}, {values: {key1: 4, key2: "cde"}}],
    b : [{values: {key1: 3, key2: "ffe"}}, {values: {key1: 11, key2: "gga"}}, {values: {key1: 7, key2: "abc"}}]
};

const results = Object.getOwnPropertyNames(data).reduce((results, key) => {
  const filtered = data[key].filter(item => item.values.key2 === 'abc');
  if (filtered && filtered.length) {
    results[key] = filtered;
  }
  return results;
}, {});

console.log(results);
Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
  • Might need to clarify with OP if they want empty results omitted but that's a nice addition – Phil Jul 01 '20 at 05:06
1
data = {
    a : [{values: {key1: 5, key2: "abc"}}, {values: {key1: 3, key2: "abc"}}, {values: {key1: 4, key2: "cde"}}],
    b : [{values: {key1: 3, key2: "ffe"}}, {values: {key1: 11, key2: "gga"}}, {values: {key1: 7, key2: "abc"}}]
}

var resp = {}
for (var key in data){
    var local = [];
    data[key].forEach(element => {
        if(element['values']['key2'] == 'abc'){
            local.push(element);
        }
    })
    resp[key] = local;
}
console.log(resp);

Response

a : [{values: {key1: 5, key2: "abc"}}, {values: {key1: 3, key2: "abc"}}],
b : [{values: {key1: 7, key2: "abc"}}]
0

As you need to iterate over your objects attributes I suggest you to extract them with Object.keys(object) which you can find here:

https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/keys

I suggest you to iterate like this:

Object.keys(data).forEach(attribute => {
        data[attribute] = data[attribute].filter(value => value.values.key2 === 'abc');
})

Good luck.

Zeitgeist
  • 81
  • 3