MortenMoulder
MortenMoulder

Reputation: 6646

"Object.Key" search using map

I honestly have no idea how to explain this in the title, but I tried my best. What I want to do is grab all the objects/values from a string. However, since my objects can contain objects, I want to make sure that works as well. This is what I want to achieve:

var key = "House.Number";
var data = "...";
var output = data.map(item => item[key]);

What this should do, is give me all the house numbers. Simply doing item["House.Number"] won't work, but if I do item["House"]["Number"] it works just fine. Is there a quick/good way of doing this? Something like split on . means new object or something like that?

https://jsfiddle.net/psz4Ltqa/1/

Upvotes: 3

Views: 1231

Answers (4)

Gor
Gor

Reputation: 2908

You need something like this. I am using this function in my project, and it works well.

  function nestedValue (obj, key) {
    var tmp = obj;
    var keys = key.split('.');
    for(var i = 0; i < keys.length && tmp; i ++) 
      tmp = tmp[keys[i]];

    return tmp;
  }

then use this in map.

    var output = data.map(item => nestedValue(item, key));

Upvotes: 0

Tolga Evcimen
Tolga Evcimen

Reputation: 7352

So basically you need this:

data.map(item => item["House"]["Number"])

But you want to achieve it with a given key. You can do this:

var keys = key.split('.'), output = data;

for (var i = 0; i < keys.length; i++) {
    output = output.map(item => item[keys[i]]);
}

console.log(output);

Upvotes: 1

user3297291
user3297291

Reputation: 23382

You can split to a path and loop through the keys like so:

var data = {
  One: {
    Two: {
      Three: 3
    }
  }
}

var getPath = function(obj, path) {
  return path
    .split(".") // Create an array of keys
    .reduce(function(pos, k) {
      return pos.hasOwnProperty(k) ? pos[k] : pos; // Return nested value or, if there isn't, last value
    }, obj);
}

console.log(getPath(data, "One.Two"));
console.log(getPath(data, "One.Two.Three"));

(Note that you can decide for your self what you want to do when paths are missing. You can for example return undefined, or like I chose to do, return the last value you could find)

Now, you can use this in a map operation like so:

// Switched the argument order to make it more suitable for `map` (data last)
var getPath = function(path, obj) {
  return path
    .split(".") // Create an array of keys
    .reduce(function(pos, k) {
      return pos.hasOwnProperty(k) ? pos[k] : pos; // Return nested value or, if there isn't, last value
    }, obj);
};

var testData = [
  { One: { Two: { Three: "Three" } } },
  { One: { Two: { Three: 3 } } },
  { One: { Two: { Three: "11" } } }
];

var oneTwoThrees = testData.map(getPath.bind(null, "One.Two.Three"));

console.log(oneTwoThrees);

If you don't like the bind syntax, you can curry the function:

// getPath now returns a function that wraps the path in its closure and waits for the object to get its data from
var getPath = function(path) {
  return function(obj) {
    return path
      .split(".") // Create an array of keys
      .reduce(function(pos, k) {
        return pos.hasOwnProperty(k) ? pos[k] : pos; // Return nested value or, if there isn't, last value
      }, obj);
  };
};

var testData = [
  { One: { Two: { Three: "Three" } } },
  { One: { Two: { Three: 3 } } },
  { One: { Two: { Three: "11" } } }
];

var oneTwoThrees = testData.map(getPath("One.Two.Three"));

console.log(oneTwoThrees);

Upvotes: 2

RomanPerekhrest
RomanPerekhrest

Reputation: 92854

According to your requirement:

What this should do, is give me all the house numbers.

Simple solution using regular for loop and String.prototype.split() function:

...
var output = data.map(function(item){
    var path = key.split('.'), val = {};
    for (var i = 0, len = path.length; i < len; i++) {
        val = val[path[i]] || item[path[i]]; 
    }
    return val;
});

console.log(output); // all house numbers

The output:

[1, 2, 3, 4]

https://jsfiddle.net/psz4Ltqa/3/

Upvotes: 2

Related Questions