1

I am looking for a HashMap sort of implementation in Javascript for JSON for the following case.

I have the JSON with following structure:

{
"level1": {
    "level2": [{
        "product1": [
            "item1",
            "item2"
        ]
    }, {
        "product2": [
            "item1",
            "item2"
        ]
    }, {
        "product3": [
            "item5",
            "item6"
        ]
    }]
}
}

For my use case, I get a value , say 'product3', I want to find values for this key i.e "item5", "item6". I have a way, that i can loop through the entire level2 object but want to know if I can simply use key to find values.

4
  • Perhaps it starts out as JSON (e.g., if you load it via ajax or read it from a data file), but by the time you're dealing with it, you've parsed it and it's not JSON anymore. JSON is a textual notation for data exchange. If you're dealing with JavaScript source code, and not dealing with a string, you're not dealing with JSON. Commented May 26, 2016 at 6:37
  • 1
    That's a very awkward structure. It's odd to have the objects in the level2 array each have a different property name for their only property. Commented May 26, 2016 at 6:38
  • 2
    Why don't you use an object for level2 instead of an array? Commented May 26, 2016 at 6:39
  • check here, I think thats what you need stackoverflow.com/questions/1946165/json-find-in-javascript Commented May 26, 2016 at 6:45

4 Answers 4

3

You can build yourself either an object or (in ES2015) a Map:

Here's an ES5 example using an object:

var map = Object.create(null);
data.level1.level2.forEach(function(entry) {
    Object.keys(entry).forEach(function(key) {
        map[key] = entry;
    });
});

Live example:

var data = {
  "level1": {
    "level2": [{
      "product1": [
        "item1",
        "item2"
      ]
    }, {
      "product2": [
        "item1",
        "item2"
      ]
    }, {
      "product3": [
        "item5",
        "item6"
      ]
    }]
  }
};
var map = Object.create(null);
data.level1.level2.forEach(function(entry) {
  Object.keys(entry).forEach(function(key) {
    map[key] = entry;
  });
});
var name = "product2";
console.log(map[name]);

We create the object (map) using Object.create(null) so that it doesn't have a prototype, and so doesn't have the pre-existing inherited properties like toString and valueOf.

The inner loop, on the result of Object.keys, is necessary because each object in the level2 array has a different name for its only property. That's an unusual and a bit awkward structure.

In ES2015 (aka "ES6") with Map, it's very similar you just use new Map and set:

var map = new Map();
data.level1.level2.forEach(function(entry) {
    Object.keys(entry).forEach(function(key) {
        map.set(key, entry);
    });
});

Live example:

var data = {
  "level1": {
    "level2": [{
      "product1": [
        "item1",
        "item2"
      ]
    }, {
      "product2": [
        "item1",
        "item2"
      ]
    }, {
      "product3": [
        "item5",
        "item6"
      ]
    }]
  }
};
var map = new Map();
data.level1.level2.forEach(function(entry) {
    Object.keys(entry).forEach(function(key) {
        map.set(key, entry);
    });
});
var name = "product2";
console.log(map.get(name));

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

8 Comments

So, is there no method JS library provides to do that ?
@CodeMonkey: This is the way you do that in JS. If you're looking for a library recommendation, those are off-topic for SO.
I was looking for something similar to this : developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
@CodeMonkey: That's what we're using in the first example: An object. I should say why I created it with Object.create(null) though.
Why is it a problem that your object-map has the usual object properties (i.e. toString and so on)?
|
1

"One-line" optimized solution using Array.some, Array.indexOf and Object.keys functions (no need to iterate through all "product" objects):

// json is your initial object
var key = 'product3', values;
json['level1']['level2'].some((v) => Object.keys(v).indexOf(key) !== -1 && (values = v[key]), values);

console.log(values);  // ["item5", "item6"]

Explanation:
arr.some(callback[, thisArg]) - This function returns true if the callback function returns a truthy value for any array element; In my example values becomes thisArg parameter which should contain values of the desired "keyName".
It will iterate through all "product" objects, but will stop looping immediately if the required keyName is found amoung the properties of the current object ( Object.keys(v).indexOf(key) !== -1 ).
As a concurrent condition it should save the values of found object which can be achieved with truthy assignment expression && (values = v[key]).
That's all.

2 Comments

This is the answer, i was looking for. IT OWRKS !! Can you pls explain as well. Would be helpful.
@CodeMonkey, added some explanation
0

You could just transform data to different structure.

const data = {
"level1": {
    "level2": [{
        "product1": [
            "item1",
            "item2"
        ]
    }, {
        "product2": [
            "item3",
            "item4"
        ]
    }, {
        "product3": [
            "item5",
            "item6"
        ]
    }]
}, 
  "level1b": {
    "level2": [{
        "product1": [
            "item1b",
            "item2"
        ]
    }, {
        "product2": [
            "item3",
            "item4"
        ]
    }, {
        "product3": [
            "item5",
            "item6"
        ]
    }]
  }
};

const transformed = Object
  .keys(data)
  .reduce((obj, key) => {
    const value = data[key];
    
    return Object.assign(obj, {[key]:  transformLvl2(value)});
  }, {});

console.log(transformed.level1.level2.product2);
console.log(transformed.level1.level2.product1);
console.log(transformed.level1.level2.product3);
console.log(transformed.level1b.level2.product1);

function transformLvl2(level2) {
  return Object
    .keys(level2)
    .reduce((obj, key) => {
      const value = level2[key];
    
      return Object.assign(obj, {[key]:  transformLvl3(value)});
    }, {});
}

function transformLvl3(level3) {
  return level3
    .reduce((obj, value) => {
      return Object.assign(obj, value);
    }, {});
}

3 Comments

How does this solve OP's pb? I don't understand your answer.
Simple, now level2 is not an array, but object, so product1 can be fetch by path transformed.level1.level2.product1 and so on.
Oh. Never mind. I didn't see it was scrollable. I only saw the beginning.
0

try (assuming that level1 and level2 is generic)

var obj = {
  "level1": {
    "level2": [{
      "product1": [
        "item1",
        "item2"
      ]
    }, {
      "product2": [
        "item1",
        "item2"
      ]
    }, {
      "product3": [
        "item5",
        "item6"
      ]
    }]
  }
};
var keyName = "product3";
var items = [];
Object.keys(obj).forEach(function(key) {
  Object.keys( obj[key] ).forEach(function(key1){               
      obj[key][key1].forEach(function(obj1){
         Object.keys(obj1).forEach(function(key2) {
            if (key2 == keyName) {
              items = items.concat(obj1[key2]);
            }
         });
      });
  });
});
console.log(items);

Comments

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.