3

Suppose I have object

let x = { "people" :{
             "Sally": "value",
             "Bob" : "other value"
                    },
          "cars" :{
             "Saab" : "this",
             "Chevy": "that"
                    },
           "trees":{
              "Birch" : "what",
              "Oak" : "where"
                    }
                }

I want to search, so if I searched for "S" I'd get back an array that was

{ "people" :{
     "Sally": "value",
          },
  "cars" :{
     "Saab" : "this",
          }
}

And if I searched for "b" I'd get:

{ "people" :{
        "Bob" : "other value"
           },
   "trees":{
        "Birch" : "what",
           }
}

or "bo" would return

    { "people" :{
        "Bob" : "other value"
     }

And if I searched "e" I'd get

{  "cars" :{
             "Chevy": "that"
            }
}

Note that the "people" and "trees" isn't caught by the search for 'e'.

The strut will be of a fixed depth, and we only ever want to catch keys that match the filter and don't have children (we're also not interested in values that match, just keys).

Adding npm dependencies is acceptable.

Attempted solution:

    filteredList(unfiltered,searchVal) {
  return unfiltered.filter(search=> {
    return search.toLowerCase().includes(searchVal.toLowerCase())
  })
}

Obviously there's more to it, but I'm not sure which direction to proceed.

5
  • updated per request Commented Apr 20, 2018 at 1:37
  • 2
    If the structure can be arbitrarily deep, you'll need to use recursion. Commented Apr 20, 2018 at 1:41
  • 1
    Just FYI, filter is an method of arrays. You have an object. Commented Apr 20, 2018 at 1:41
  • I could remove the "arbitrary depth" constraint. I was trying to think future-proof, but right now my data is consistently 5 levels deep. I will update the question, thank you @Amy. Commented Apr 20, 2018 at 1:44
  • I took that out, but, e.g. here's the head of the actual data: "Configs": { "Head Configs": { "Configuration": { "hdCfgWidth": { "Name": "Head Width", "Value": 3, .... Your solution will work, I just need to build it out to the right depth Commented Apr 20, 2018 at 1:53

2 Answers 2

4

This is a basic attempt. It's seems work quite fine but I'm very sure that it may be possible to make even better. But I don't know how.

let x = {
  "people": {
    "Sally": "value",
    "Bob": "other value"
  },
  "cars": {
    "Saab": "this",
    "Chevy": "that"
  },
  "trees": {
    "Birch": "what",
    "Oak": "where"
  }
}

let search = "B";

let result = {};

for (let key in x) {
  let tmp = {};
  for (let subKey in x[key]) {
    if (subKey.includes(search)) { // Use startsWithif you want even substring that aren't at the beginning
      tmp[subKey] = x[key][subKey];
    }
    if (Object.keys(tmp).length > 0) {
      result[key] = Object.assign({}, tmp);
    }
  }
}

console.log(result)

If you want an arbitrary deep, you should try with some kind of recursion, but that will be more complex.

Sign up to request clarification or add additional context in comments.

3 Comments

No need for a JSFiddle when you can use a stack snippet.
@GerardoFurtado Thanks!!
Wow. Thank you so much, that works exactly the way I was trying to do.
1

Here's a solution that uses the lodash library. Lodash has a number of super useful methods for object manipulation, in this case _.pickBy and _.mapValues can be used to filter your nested object.

Example:

let x = {
  "people": {
    "Sally": "value",
    "Bob": "other value"
  },
  "cars": {
    "Saab": "this",
    "Chevy": "that"
  },
  "trees": {
    "Birch": "what",
    "Oak": "where"
  }
}

function search(query) {
    query = query.toLowerCase();
    const matched = _.mapValues(x, v => _.pickBy(v, (_, k) => k.toLowerCase().includes(query)));
    return _.pickBy(matched, v => Object.keys(v).length)
}

console.log(search("S"))
console.log(search("b"))
console.log(search("bo"))
console.log(search("e"))
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

5 Comments

Disclaimer, the solution for "b" includes one additional result ("Saab") than your example, not sure if you meant to leave it out.
lol no, you're right, my "filter by eye" method missed that. This is a really neat solution! Thanks!
Use startsWith instead of includes.
@EnderLook Wouldn't that break OPs "e" case, which should return "Chevy" even though the match is not at the start?
@CRice, ohh, I haven't noticed that. I'm very sorry.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.