1

I have an object and I want to build up a function, take the object and a property name and the function returns the value of that property, if you see that you gonna say, "Oh it's so easy, just use a double quotation or dot notation to access that value" BUT I want the function to return me not only the first level property value, But also their nested if there's any object inside that object and search for that propertyName if exists accumulate the value in the array, and return the result:

let obj = {
  a: {
    b: "value of b",
  },
  b: "another value of b",
  c: {
    d: [1, 2, 3],
  },
};
//                        input    output
getDeepValuesProperty(obj, "a"); //{ b: "value of b" }
getDeepValuesProperty(obj, "b"); //["value of b", "another value of b"]
getDeepValuesProperty(obj, "d"); //[1, 2, 3]

let obj = {
  a: {
    b: "value of b",
  },
  b: "another value of b",
  c: {
    d: [1, 2, 3],
  },
};

function getDeepValuesProperty(object, propertyName) {
  if (object[propertyName]) {
    return object[propertyName];
  } else if (typeof object[propertyName] == "object") {
    return getDeepValuesProperty(object[propertyName], propertyName);
  }
}
console.log(getDeepValuesProperty(obj, "a")); //{ b: "value of b" }
console.log(getDeepValuesProperty(obj, "b")); //another value of b
console.log(getDeepValuesProperty(obj, "d")); //undefined

The first example worked, but the second doesn't work as I expect and return just the last value, the third example return undefined I don't know why this happens, do I miss anything in the recursion function?

XMehdi01
  • 5,538
  • 2
  • 10
  • 34
  • For it to work, you have to use `getDeepValuesProperty(obj, "c.d"))`, not just `"d"` – Staxlinou Jul 30 '22 at 13:26
  • But `"d"` just a property that I want its value wherever it is, since it exists I should get its value! – XMehdi01 Jul 30 '22 at 13:32
  • Your tests inside the function are wrong. For example `(obj, "d")` tests if there is `obj->d`. If not, you test if `obj->d` is an object. But that doesn't exist in first level, because there you only have `obj->c`, so it fails and returns undefined. You have to build a function that dives into every object it encounters untill there are no more objects (that's recursion) . Even if on the first level you have `obj->b`, you still can have `obj->b->b->b`. Maybe the answers on [this question](https://stackoverflow.com/q/15690706/1685196) can help you. – Michel Jul 30 '22 at 13:59

5 Answers5

3

Your getDeepValuesProperty() function instantly return your object value if one of if your statements match. If you need all the occurrences, you need to aggregate these results, using a .reduce function for example.

let obj = {
  a: {
    b: "value of b",
  },
  b: "another value of b",
  c: {
    d: [1, 2, 3],
  },
};

function getDeepValuesProperty(object, propertyName) {
    return Object.entries(object).reduce((values, [key, value]) => {
        if (key === propertyName) {
            return [...values, value];
        } else if (typeof value === 'object') {
            return [...values, ...getDeepValuesProperty(value, propertyName)];
        }
        return values;
    }, []);
}

console.log(getDeepValuesProperty(obj, 'a')); // [ { b: 'value of b' } ]
console.log(getDeepValuesProperty(obj, 'b')); // [ 'value of b', 'another value of b' ]
console.log(getDeepValuesProperty(obj, 'd')); // [ [ 1, 2, 3 ] ]
1

Use a wrapper function in which you declare an empty array, and push results to that array and return it in the end.

let obj = {
  a: {
    b: "value of b",
  },
  b: "another value of b",
  c: {
    d: [1, 2, 3],
  },
};

function getDeepValuesProperty(object, propertyName) {
  const output = [];
  
  function getDeep(object, propertyName) {
    for (let prop in object) {
      if (prop === propertyName) output.push(object[prop])
      if (typeof object[prop] === 'object') {
        getDeep(object[prop], propertyName)
      }
    }
  }
  
  getDeep(object, propertyName);
  return output
}

console.log(getDeepValuesProperty(obj, "a")); //{ b: "value of b" }
console.log(getDeepValuesProperty(obj, "b")); // "value of b", "another value of b"b
console.log(getDeepValuesProperty(obj, "d")); // [1,2,3]
Brother58697
  • 2,290
  • 2
  • 4
  • 12
1

You can split an object into entries as follows:

Object.entries({a: 42, b: 43})
// [['a', 42], ['b', 43]]
//    ^   ^      ^   ^
//    key value  key value

You can then iterate the list of entries and make some decisions:

Property name is…

  1. a match and value is neither an array nor an object: keep value
  2. a match and value is an array: keep array and inspect each element
  3. a match and value is an object: keep object and inspect object

Property name is not a match and…

  1. value is neither an array nor an object: discard value
  2. value is an array: inspect each element
  3. value is an object: inspect object

Every time you inspect you make a recursive call on that value. The result of each call is aggregated into an array.

Here's a curried recursive solution that gets the value of all a properties at any depth of an object including objects contained in arrays:

const get_all_properties_by_name = name => function loop(obj) {
  return Object.entries(obj).flatMap(([k, v]) => {
    const match = k == name;
    const is_arr = Array.isArray(v);
    const is_obj = typeof v == 'object' && v !== null;
    if (match && is_arr) return [v].concat(v.flatMap(loop));
    if (match && is_obj) return [v].concat(loop(v));
    if (match) return [v];
    return is_arr ? v.flatMap(loop) : loop(v);
  });
}

const get_a = get_all_properties_by_name('a');

And here's some results:

get_a({ a: 42
      , b: { a: 43 }
      , c: { a: 44
           , d: { a: 45
                , e: { a: 46 } }}
      , d: [ { a: 47 }
           , { b: { a: 48 }}
           , { c: 100 }]
      , e: { f: [ { a: 49 }
                , { b: { c: { d: { a: 50 }}}}]}
      , f: { a: { a: { a: 51 }}}
      , g: { a: [ 52
                , 53 ]
           , b: { a: [ 54
                     , 55
                     , { a: 56 }
                     , { b: { a: 57 }}
                     , { a: [ 58
                            , 59
                            , [{ a: 60 }] ]} ]}}
      , h: 61});

/*
[ 42
, 43
, 44
, 45
, 46
, 47
, 48
, 49
, 50
, {a: {a: 51}}
, {a: 51}
, 51
, [52, 53]
, [54, 55, {a: 56}, {b: {a: 57}}, {a: [58, 59, [{a: 60}]]}]
, 56
, 57
, [58, 59, [{ a: 60 }]]
, 60 ]
*/
customcommander
  • 17,580
  • 5
  • 58
  • 84
0

Just a simple recursion

const obj = {a: {b: "value of b",},b: "another value of b",c: {d: [1, 2, 3]}};


const getDeepValuesProperty = (obj, targetKey) =>  
  Object.entries(obj).reduce((acc, [key, value]) => {
    if (key === targetKey) return value;

    const goDeep = (
      typeof value === 'object' && !Array.isArray(value) && value !== null && acc === null
    );
    if (goDeep) return getDeepValuesProperty(value, targetKey);

    return acc;
  }, null);

console.log(getDeepValuesProperty(obj, 'a'));
console.log(getDeepValuesProperty(obj, 'b'));
console.log(getDeepValuesProperty(obj, 'd'));
.as-console-wrapper { max-height: 100% !important; top: 0 }
A1exandr Belan
  • 4,442
  • 3
  • 26
  • 48
0

I think generators are a good fit for this problem -

function *deepValues(t, prop) {
  switch (t?.constructor) {
    case Object:
      for (const [k,v] of Object.entries(t)) 
        if (k == prop)
          yield v
        else
          yield *deepValues(v, prop)
      break
    case Array:
      for (const v of t)
        yield *deepValues(v, prop)
      break
  }
}

const obj = {
  a: {
    b: "value of b",
  },
  b: "another value of b",
  c: {
    d: [1, 2, 3, {a: "hello", b: "bee"}],
  },
}

console.log(Array.from(deepValues(obj, "a")))
console.log(Array.from(deepValues(obj, "b")))
console.log(Array.from(deepValues(obj, "d")))
.as-console-wrapper { min-height: 100%; top: 0; }
[
  {
    "b": "value of b"
  },
  "hello"
]
[
  "value of b",
  "another value of b",
  "bee"
]
[
  [
    1,
    2,
    3,
    {
      "a": "hello",
      "b": "bee"
    }
  ]
]
Mulan
  • 129,518
  • 31
  • 228
  • 259