-2

I'm trying to filter a nested json object by keys based on user inputs, but the amount of keys can be completely dynamic. I also need the search to be based on index / depth.

For example if you take this json:

values = {
  1: {
    4: {
      9: {
         ...
      },
      10: {
         ...
      }
    },
    7: {
      12: {
         ...
      },
      15: {
         ...
      },
      18: {
         ...
      }
    }
  },
  2: {
    5: {
      7: {
         ...
      }
    }
  }
}

I want the following results:

search = [1]

result:
{
  1: {
    4: {
      9: {
         ...
      },
      10: {
         ...
      }
    },
    7: {
      12: {
         ...
      }
      15: {
         ...
      }
      18: {
         ...
      }
    }
  }
}

search = [1, 4]

result:
{   
    4: {
        9: {
          ...
        },
        10: {
          ...
        }
    }
}


search = [1, 4, 9]

result:

{ 
  9: {
      ...
  }
}


search = [2, 5]

result:
{   
    5: {
       7: {
          ...
       }
    }
}

I initially thought I could just go result = values[2][5] but as the keys and json are completely dynamic in both content and depth it's not going to work.

I've tried using filter but it only works at one level as I need to know the X previous keys first.

Anyone have any ideas?

Edit

This was marked as a duplicate of this post Search key in nested complex JSON but it's not quite the same as I am not searching the entire object for the first result of that key. The keys aren't always unique, e.g. they could be elsewhere in the whole object, so I need to traverse through the object in order.

Onfire
  • 336
  • 3
  • 13
  • `result = {5: values[2][5]};`? – solarc Jun 22 '18 at 22:20
  • Write a function that can search for a single key. Then loop over your input array, calling it on the result of the previous call. – Barmar Jun 22 '18 at 22:21
  • What does it mean when a result begins with `5:`? That's not valid Javascript syntax, it needs to be inside an object, e.g. `{5: ...}`? – Barmar Jun 22 '18 at 22:22
  • @solarc That won't work if `2` is nested more deeply. – Barmar Jun 22 '18 at 22:22
  • Can you post your attempts? – jhpratt Jun 22 '18 at 22:24
  • @Barmar, in the examples the search data always starts from the root (2 was at the same level as 1), we'll need clarification if the arguments are a search of keys across all levels or just traversing from the root. – solarc Jun 22 '18 at 22:28
  • @solarc Read the line beginning with "I initially thought". It explains why that doesn't work for him. – Barmar Jun 22 '18 at 22:30
  • @Barmar sorry yes that is just my bad formatting in the question - the result would be a json object {5: ...} – Onfire Jun 22 '18 at 22:31
  • @solarc the search is always traversing, but the problem is that we don't know how deep to go – Onfire Jun 22 '18 at 22:31
  • The linked duplicated answer is much more complex than this. Traversing is simpler: `function search(l, k) {t = l; for (i of k) { t = t[i];} return {[k.pop()]: t}}` (this still needs some error checking for nonexistent keys) – solarc Jun 22 '18 at 22:44
  • For what its worth the answer was embarrassingly simple: https://codepen.io/anon/pen/KeedGL I should know better... – Onfire Jun 22 '18 at 23:16

1 Answers1

0

With JSON, you can still use arrays. If you want to select by index, you'll have to write your JSON like so.

[
  [
    { 'key' : 'value' },
    { 'key' : 'value' }
  ],
  [
    { 'key' : 'value' },
    { 'key' : 'value' }
  ]
],
[ ... ],
[ ... ]

Assuming your JavaScript then assigned this JSON to const values, you would then be able to access any of these objects with the syntax values[0][1]. This way you won't have to do any additional work to make the indexes dynamic, because that's their standard behavior.

As far as dynamic depth goes, you could write a short function using the handy Array.prototype.every().

const search = (searchArr, idxArr) => {
  let result = searchArr
  const exists = () => idxArr.every(idx => result = result[idx])
  return exists() ? result : null
}

Or ES5, if you prefer:

var search = function(searchArr, idxArr) {
  var result = searchArr;
  var exists = function() {
    return idxArr.every(function (idx) {
      return result = result[idx];
    });
  };
  return exists() ? result : null;
};

Check out the example on codepen.