2

Suppose I got an object defined as such:

const me = {
    id: 1,
    name: 'James',
    age: 40,
    family: {
        mother: {
            id: 101,
            name: 'Bea',
            age: 66
        },
        father: {
            id: 102,
            name: 'Martin',
            age: 69
        },
        children: [
            {
                id: 11,
                name: 'Tom',
                age: 18,
            },
            {
                id: 12,
                name: 'Nancy',
                age: 13,
            },
        ],
    },
}

How does one easily access a value by just giving an array of strings of the chained properties' names? For example, calling:

search(me, ['family', 'father', 'age'])

which would be the same as:

me['family']['father']['age']

would return 69.

PS: What about having search(me, ['family', 'children', 'name']) return ['Tom', 'Nancy']?

PSS: Or even search(me, ['family', 'children', ['name', 'age']]) returning

[
    {
        name: 'Tom',
        age: 18
    },
    {
        name: 'Nancy',
        age: 13
    }
]

EDIT: I went checking out lodash/deepdash libraries, but couldn't really figure it out by myself.

Daniel
  • 47
  • 5
  • Have a look at [Accessing nested JavaScript objects and arrays by string path](https://stackoverflow.com/q/6491463/218196) except that in your case you have already have the array (no need to split the path). – Felix Kling Apr 07 '21 at 18:35
  • What have you tried so far? Have you tried writing an utility function for this, or something? – Jeremy Thille Apr 07 '21 at 18:36
  • FYI, this is not Java, so this: `{'family', 'father', 'age'}` is not an array. – Randy Casburn Apr 07 '21 at 18:36
  • You should ask one question at a time, I've answered the first one but your PS and PSS are separate questions – Guerric P Apr 07 '21 at 18:43

3 Answers3

3

You can do that with this simple recursive function which takes an array as the query:

const me = {
    id: 1,
    name: 'James',
    age: 40,
    family: {
        mother: {
            id: 101,
            name: 'Bea',
            age: 66
        },
        father: {
            id: 102,
            name: 'Martin',
            age: 69
        },
        children: [
            {
                id: 11,
                name: 'Tom',
                age: 18,
            },
            {
                id: 12,
                name: 'Nancy',
                age: 13,
            },
        ],
    },
}


function search(obj, [first, ...rest]) {
  return rest.length ? search(obj[first], rest) : obj[first];
}

const result = search(me, ['family', 'father', 'age']);

console.log(result);
Guerric P
  • 30,447
  • 6
  • 48
  • 86
0

Though it's bit lengthy, you could try this for all your combinations.

const me={id:1,name:'James',age:40,family:{mother:{id:101,name:'Bea',age:66},father:{id:102,name:'Martin',age:69},children:[{id:11,name:'Tom',age:18,},{id:12,name:'Nancy',age:13,},],},}

function search(data, searchPattern) {
    const keys = [...searchPattern];
    
    //picking last key and it's corresponding value
    const lastKey = keys.pop();
    const resultInst = keys.reduce((acc,key)=>{
        return acc[key];
    }, data);
    
    // if it's array iterating it further to construct the response
    if (Array.isArray(resultInst)) {
        return resultInst.map(inst => {
            if (Array.isArray(lastKey)) {
                return lastKey.reduce((accInner,key) => {
                    accInner[key] = inst[key];
                    return accInner;
                }, {});
            } else {
                return inst[lastKey];
            }
        });
    } else {
        // else just returning property's value
        return resultInst[lastKey];
    }
}

console.log(search(me, ['family', 'father', 'age']))
console.log(search(me, ['family', 'children', 'name']))
console.log(search(me, ['family', 'children', ['name', 'age']]))
Ravikumar
  • 2,085
  • 12
  • 17
0

Some interesting requirements you have there! Here is an all-in-one answer using object-scan and lodash

.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/lodash@4.17.21"></script>
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan@18.1.2/lib/index.min.js';

const myData = { id: 1, name: 'James', age: 40, family: { mother: { id: 101, name: 'Bea', age: 66 }, father: { id: 102, name: 'Martin', age: 69 }, children: [{ id: 11, name: 'Tom', age: 18 }, { id: 12, name: 'Nancy', age: 13 }] } };

const search = (data, needle) => objectScan([needle], {
  reverse: false,
  rtn: ['key', 'value'],
  useArraySelector: false,
  afterFn: (state) => {
    if (state.result.length === 0) {
      state.result = undefined;
    } else if (needle.includes('{')) {
      const prefixLength = lodash
        .zip(...state.result.map(([k]) => k))
        .findIndex((e) => new Set(e).size > 1);
      const result = [];
      state.result.forEach(([k, v]) => lodash.set(result, k.slice(prefixLength), v));
      state.result = result;
    } else if (
      state.result.length === 1
      && state.result.every(([k]) => k.every((p) => typeof p === 'string'))
    ) {
      state.result = state.result[0][1];
    } else {
      state.result = state.result.map(([k, v]) => v);
    }
  }
})(data);

console.log(search(myData, 'family.father.age'));
// => 69

console.log(search(myData, 'family.children.name'));
// => [ 'Tom', 'Nancy' ]

console.log(search(myData, 'family.children.{age,name}'));
// => [ { name: 'Tom', age: 18 }, { name: 'Nancy', age: 13 } ]

console.log(search(myData, 'family.father.unknown'));
// => undefined
</script>

Disclaimer: I'm the author of object-scan

vincent
  • 1,953
  • 3
  • 18
  • 24