Thanasis Ioannidis
Thanasis Ioannidis

Reputation: 3231

Dynamically create a method from a string in ES6 javascript (non browser)

In javascript code, I have a string that defines a property path within an object. For instance

var def = "contact.email"

How can I get the following function from this string?

o => o.contact.email

So that I can use it in the following way:

var person = {
    name: 'Test',
    contact: { email: '[email protected]' }
}

var emailGetter = MagicCompileFunction('contact.email');

var email = emailGetter(person);
// here, email should hold the value of person.contact.email

The path string is unknown at compile time. It could be provided by the user as well.

The solution should work in non-browser environments too (where there is no window object), for instance in NodeJS server side javascript.

I know that one solution would be to create a generic method that takes an object and a string as arguments (valueGetter(person, "contact.email") for instance), where the string defines the path within the object, and then split the string on each dot '.' and follow the path on the object. But I don't want this algorithm to execute in every call to getter function. I need a dynamically compiled method that would give me a final getter that would access the desired (sub)property immediately.

Upvotes: 0

Views: 1812

Answers (4)

mikeapr4
mikeapr4

Reputation: 2856

Slightly more ES6 than Archer's answer:

function MagicCompileFunction(mapping) {
	return (obj => mapping.split(".").reduce((curr, name) => curr[name], obj));
}

var person = {
    name: "Test",
    contact: {
		email: "[email protected]"
	}
}

var emailGetter = MagicCompileFunction("contact.email");
var email = emailGetter(person);

var nameGetter = MagicCompileFunction("name");
var name = nameGetter(person);

console.log(email);
console.log(name);

Upvotes: 3

Redu
Redu

Reputation: 26161

You may dynamically access the nested object properties simply as follows;

function dynPropAcc(o,s){
  return s.split(".")
          .reduce((p,q,i) => i-1 ? p[q] : o[p][q]);
}

var def = "contact.email.2",
    obj = {contact: {name: "John", email: { 1: "[email protected]", 2: "[email protected]"}}};

console.log(dynPropAcc(obj,def));

Upvotes: 0

NiRUS
NiRUS

Reputation: 4259

You can achieve this using Function constructor

var person = {
    name: 'Test',
    contact: { email: '[email protected]' }
}
var maginFunction = (def) => new Function('o','return o.'+ def);

var emailGetter = maginFunction('contact.email');

var email = emailGetter(person);
console.log(email);

Upvotes: 2

Reinstate Monica Cellio
Reinstate Monica Cellio

Reputation: 26143

I like the question so I came up with a solution. This should do what you ask, without any need for using eval...

function MagicCompileFunction(mapping) {
	mapping = mapping.split(".");
	return (function(obj) {
		var result = obj;
		for (var idx in mapping) {
			result = result[mapping[idx]];
		}
		return result;
	});
}

var person = {
    name: "Test",
    contact: {
		email: "[email protected]"
	}
}

var emailGetter = MagicCompileFunction("contact.email");
var email = emailGetter(person);

var nameGetter = MagicCompileFunction("name");
var name = nameGetter(person);

console.log(email);
console.log(name);

The MagicCompileFunction() returns a function that is primed with the mapping that you use when you create the getter object. Then you can pass any person into that object to return the related data.

Upvotes: 1

Related Questions