var give = 'i.want.it';
var obj = {
i: {
want: {
it: 'Oh I know you do...'
}
}
};
console.log(obj[give]); // 'Oh I know you do...'
Can I somehow get the object string using a path String of some sort? I'm trying to store a relationship in a database where the field it can't be in it's own document.
Use Array#reduce() method
var give = 'i.want.it';
var obj = {
i: {
want: {
it: 'Oh I know you do...'
}
}
};
var res = give.split('.').reduce(function(o, k) {
return o && o[k];
}, obj);
console.log(res);
This will work :
var give = 'i.want.it';
var obj = {
i: {
want: {
it: 'Oh I know you do...'
}
}
};
console.log( eval("obj."+give));
Live DEMO JSFiddle
This is a really easy way to do it, but not safe, i don't advise you to use it for professional use. Use previous answer they looks good.
make it
var obj = {
i: {
want: {
it: 'Oh I know you do...'
}
}
};
//var result = JSON.parse(JSON.stringify(obj)); //cloning the existing obj
var result = obj; //cloning the existing obj
var give = 'i.want.it';
//now split the give and iterate through keys
give.split(".").forEach(function(key){
result = result[key];
});
console.log(result);
You can use eval()
var obj = {"a": { "b": { "c": 3}}};
writeln(eval('obj.a.b.c') + 2);
This will output 5.
JavaScript is weakly typed and thus it's evaluation function executes a statement as well as evaluating an expression.
Related
Hi everyone I need to write a function that takes a string and object and interpolates that object in the string so something like this
// interpolate("Its {weather} outside", {weather: 'damn Hot'})
// returns 'It's damn hot outside'
// interpolate( "Hi my name is {name}", {name: 'John'});
// returns 'Hi my name is John'
It should also no matter how deep the object goes so a case like this
// interpolate("Hi my name is {contact.name}", {contact: {name: 'john'}});
Im a little stuck and at first I tried splitting up the string into an array then trying to fit the object value in the array then join that back together but that has not worked for all test cases, can someone help me write this function and explain their solution please? Thankyou
so I tried something like this but does not really work for all test cases, this is a template literal but the function should just work giving those values as arguments on its own, otherwise I'm pretty stuck . .
function interpolate(strings, ...keys) {
return (function(...values) {
var dict = values[values.length - 1] || {};
var result = [strings[0]];
keys.forEach(function(key, i) {
var value = Number.isInteger(key) ? values[key] : dict[key];
result.push(value, strings[i + 1]);
});
return result.join('');
});
}
var t1Closure = interpolate`${0}${1}${0}!`;
t1Closure('Y', 'A'); // "YAY!"
var t2Closure = interpolate`${0} ${'foo'}!`;
console.log(t2Closure('Hello', {foo: 'World'})); // "Hello World!"
ok I'm getting closer so I separated the problem into two functions and need to combine them, the only thing is I'm not sure how to get the last use case to work without template literals
var something = "something";
var sub = function(str) {
return str.replace(/#\{(.*?)\}/g,
function(whole, expr) {
return eval(expr)
})
}
console.log(sub("hello #{something}"));
var objectValue = function(str, obj){
for(key in obj) {
if(obj.hasOwnProperty(key)) {
var value = obj[key];
return str + value;
}
}
}
console.log(objectValue("hi my name is ", {contact: {name: 'john'}}));
Using the dreaded eval
If you control the string that you pass and consider it safe, you can use eval:
function interpolate (str, obj) {
return str.replace(/\{(.*?)\}/g, function (_, ref) {
return eval('obj.' + ref);
});
}
var output = interpolate("Its {weather} outside", {weather: 'damn Hot'});
console.log(output);
output = interpolate("Hi my name is {contact.name}", {contact: {name: 'john'}});
console.log(output);
output = interpolate("The highest bid is {rank[0].bid}", {rank: [{bid: 900}, {bid:600}]});
console.log(output);
Be aware that if given a string like '{x;alert("hi")}', the above function would execute that alert, or any code that is put instead. So this is not a good solution if the string is provided (or can be altered) by the user.
Template Literals
It does not follow your function descriptor, but template literals already offer the functionality you are looking for:
var weather = 'damn Hot';
var output = `It's ${weather} outside`;
console.log(output);
var contact = {name: 'john'};
var output = `Hi my name is ${contact.name}`;
console.log(output);
Here you go:
'use strict';
function interpolate(str, obj) {
for (let prop in obj) {
if (obj.hasOwnProperty(prop)) {
str = str.replace(new RegExp(`{${prop}}`, 'g'), obj[prop]);
}
}
return str;
}
console.log(interpolate("Its {weather} outside", {weather: 'damn Hot'}));
// Its damn Hot outside
Try renderjs.
$("<p>Its {{:weather}} outside</p>").render({weather: 'damn Hot'})
-> <p>Its damn Hot outside</p>
Or lodash templates. Lodash already has a lot of handy features you end up using a lot of in my opinion.
var str = _.template('Its <%= weather %> outside')({weather: 'damn Hot'});
// If you wanted to use the {{:var}} syntax.
_.templateSettings.interpolate = /{{:([\s\S]+?)}}/g;
var str = _.template('Its {{:weather}} outside')({weather: 'damn Hot'});
A little late to the party, but in case you cannot (don't want to) use any external libraries or ES6, here is the idea:
function getMatches(s) {
var regExp = /{([^}]*)}/g,
matches = [],
match;
while ((match = regExp.exec(s)) != null) {
matches.push(match[1]);
}
return matches;
}
function objValue(obj, i) {
return obj[i];
}
function interpolate(s, obj) {
var matches = getMatches(s),
result = s;
matches.forEach(function (match) {
var props = match.split('.'),
value = props.reduce(objValue, obj) || '';
result = result.replace('{' + match + '}', value);
});
return result;
}
Usage
interpolate("Its {weather} outside", { weather: 'damn Hot' });
JSFiddle.
In javascript, I want to select a certain property by it's name, which is stored in a string. I know that window[someString] is the way to go. It works when someString = "somevariable", but unfortunately, my program will also have strings such as someobject.someproperty. This does not work.
So the question is, given code
someString = "one.two.three";
one = {
two: {
three: "This is the value that I want to get"
}
};
// window[someString] does not work.
, how can I get the value of one.two.three using the value of someString, without using eval?
Use split and make a recursive method
var someString = "one.two.three";
var keys = someString.split('.');
one = {
two: {
three: "This is the value that I want to get"
}
};
function getinnerProperty(object, keys) {
var key = keys.shift();
if (keys.length) {
var nestedObject = object[key];
return getinnerProperty(nestedObject, keys);
} else {
return object[key];
}
}
console.log(getinnerProperty(window, keys));
You can write a function that uses split and iteratively traverses the object tree:
var someString = "one.two.three";
var one = {
two: {
three: "This is the value that I want to get"
}
};
function getValue(keyStr) {
var keys = keyStr.split('.');
var result = global;
for (var i = 0; i < keys.length; i++) {
result = result[keys[i]];
}
return result != global ? result : undefined;
}
getValue(someString);
I have an object in javaScript:
var stuffObject = {
stuffArray1 : [object1, object2, object3],
stuffArray2 : [object4, object5, object6]
}
object1 to 6 look like this:
object1 = {
dataStuff : {
stuffId: "foobar"
}
}
My question: given the key "foobar", how do I retrieve object1 from the stuffObject using jQuery? The key "stuffId" always has a unique value.
You won't get around iterating over the set to find the object you are looking for. jQuery can't really help with that. Its purpose is DOM manipulation. If you want functionality to deal with objects, sets, lists, etc., check out lodash.
I wrote a function to deal with the problem. I hope it's understandable.
var stuffObject = {
stuffArray1 : [{dataStuff: {stuffId: 'foobar'}}, {dataStuff: {stuffId: 'foo'}}, {}],
stuffArray2 : [{}, {dataStuff: {stuffId: 'bar'}}, {}]
}
function getObjByStuffId(stuffObject, stuffId) {
var key, arr, i, obj;
// Iterate over all the arrays in the object
for(key in stuffObject) {
if(stuffObject.hasOwnProperty(key)) {
arr = stuffObject[key];
// Iterate over all the values in the array
for(i = 0; i < arr.length; i++) {
obj = arr[i];
// And if it has the value we are looking for
if(typeof obj.dataStuff === 'object'
&& obj.dataStuff.stuffId === stuffId) {
// Stop searching and return the object.
return obj;
}
}
}
}
}
console.log('foobar?', getObjByStuffId(stuffObject, 'foobar') );
console.log('foo?', getObjByStuffId(stuffObject, 'foo') );
console.log('bar?', getObjByStuffId(stuffObject, 'bar') );
Thanks for the help guys, using the input of other people I have solved it myself:
getStuffById: function(id){
for (stuffArray in stuffObject) {
for (stuff in stuffObject[stuffArray]) {
if (stuffObject[stuffArray][stuff].dataStuff.stuffId == id) {
return stuffObject[stuffArray][stuff];
}
}
}
return null;
}
This also works better than the (now deleted) answer that uses .grep(), as this function terminates as soon as it finds the correct object.
I'd like to have a set of objects in Javascript. That is, a data structure that contains only unique objects.
Normally using properties is recommended, e.g. myset["key"] = true. However, I need the keys to be objects. I've read that Javascript casts property names to strings, so I guess I can't use myset[myobject] = true.
I could use an array, but I need something better than O(n) performance for adding, finding and removing items.
It needs to be able to tell objects apart by reference only, so given:
var a = {};
var b = {};
then both a and b should be able to be added, because they're separate objects.
Basically, I'm after something like C++'s std::set, that can store Javascript objects. Any ideas?
ES6 provides a native Set:
let s = new Set();
let a = {};
let b = {};
s.add(a);
console.log(s.has(a)); // true
console.log(s.has(b)); // false
Here's a mad suggestion ... key it on the result of JSON.stringify(object)
It's not possible for all objects, but if your object has a .toString() method implemented, it is:
var x = {toString: function(){ return 'foo'; }};
var y = {toString: function(){ return 'bar'; }};
var obj = {};
obj[x] = 'X';
obj[y] = 'Y';
console.log(obj);
// { foo: 'X', bar: 'Y' }
If you want to make this easier, make it a class:
function myObj(name){
this.name = name;
}
myObj.prototype.toString = function(){ return this.name; }
var obj = {};
obj[new myObj('foo')] = 'X';
obj[new myObj('bar')] = 'Y';
I'm answering my own question, but I came up with an alternative solution I thought was interesting and thought it would be useful to share it.
cwolves' answer gave me an idea. Providing an object's toString() method uniquely identifies the instance, properties of an object can be used to store a set of objects. Essentially, to store object x, you can use items[x.toString()] = x;. Note that the value is the object itself, so then the set of objects can be extracted by looking at all item's properties and dumping all the values in to an array.
Here's the class, which I call ObjectSet, in full. It requires objects are uniquely identified by their toString() method, which is OK for my purposes. add, remove and contains should all run in better than O(n) time - whatever javascript's property access efficiency is, which hopefully is either O(1) or O(n log n).
// Set of objects. Requires a .toString() overload to distinguish objects.
var ObjectSet = function ()
{
this.items = {};
this.item_count = 0;
};
ObjectSet.prototype.contains = function (x)
{
return this.items.hasOwnProperty(x.toString());
};
ObjectSet.prototype.add = function (x)
{
if (!this.contains(x))
{
this.items[x.toString()] = x;
this.item_count++;
}
return this;
};
ObjectSet.prototype.remove = function (x)
{
if (this.contains(x))
{
delete this.items[x.toString()];
this.item_count--;
}
return this;
};
ObjectSet.prototype.clear = function ()
{
this.items = {};
this.item_count = 0;
return this;
};
ObjectSet.prototype.isEmpty = function ()
{
return this.item_count === 0;
};
ObjectSet.prototype.count = function ()
{
return this.item_count;
};
ObjectSet.prototype.values = function ()
{
var i, ret = [];
for (i in this.items)
{
if (this.items.hasOwnProperty(i))
ret.push(this.items[i]);
}
return ret;
};
I used Map, solved my case
const objectsMap = new Map();
const placesName = [
{ place: "here", name: "stuff" },
{ place: "there", name: "morestuff" },
{ place: "there", name: "morestuff" },
];
placesName.forEach((object) => {
objectsMap.set(object.place, object);
});
console.log(objectsMap);
For what you're trying to do (sets of objects), there is no native Javascript implementation. You would have to implement this on your own. One way to do this would be to implement a hashing function for your objects. The backing data-type of the set would be an associative array, where the key of the array is the value you get from calling the object's hash function, and the value of the array is the object itself.
Of course, this doesn't address the issue that you highlighted, so you will need to take equality into account as well (implement an equals function perhaps)?
Instead of making the hash function a property of the object itself, you can have a standalone hash function that takes in an object as input and generates a hash value (presumably by iterating over its properties).
Using this method you should be able to get O(1) for insertion, searching, and removing (not counting the order of the hash function, which shouldn't be any worse than O(n), especially if you are iterating over its properties to create your hashed value).
ECMAScript6 Set should behave like that:
Standard: http://www.ecma-international.org/ecma-262/6.0/#sec-set-o-p-v-throw
Unofficial ES6 cheat sheet: https://github.com/lukehoban/es6features#map--set--weakmap--weakset
Working example on Firefox 32 (but not implemented in Chromium 37):
if (Set) {
var s = new Set()
var a = {}
var b = {}
var c = {}
s.add(a)
s.add(b)
s.add(b)
assert(s.size === 2)
assert(s.has(a))
assert(s.has(b))
assert(!s.has(c))
}
This is not surprising since {} != {}: equality compares object addresses by default.
A module that implements it for browsers without support: https://github.com/medikoo/es6-set
Javascript Set's don't do deep object comparison.
Using lodash, this is a unique array with deep object comparison:
const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.uniqWith(objects, _.isEqual);
Just typed this up, it's only briefly tested:
var Set = function Set()
{
var list = [];
var contains;
this.contains = contains = function(x) {
return list.indexOf(x) >= 0;
}
var put;
this.put = put = function(x) {
if (!contains(x))
list.push(x);
return this;
}
var remove;
this.remove = remove = function(x)
{
var idx = list.indexOf(x);
if (idx >= 0)
list.splice(idx,1);
return this;
}
var all;
this.all = all = function()
{
return list.concat();
}
return this;
}
It seems that the inner call of function works when prefixed with this.
Exemple:
var put;
this.put = put = function(x) {
if (!this.contains(x))
list.push(x);
return this;
}
Please use this code as a reference.
const fruits = [
{name: 'apple', price: 100},
{name: 'apple', price: 100},
{name: 'orange', price: 200},
{name: 'grapes', price: 300}
];
const hasFruitDuplicated = () => {
const duplicatedDeleteFruits = fruits.filter((fruit, index) =>
fruits.findIndex(item => item.name === fruit.name && item.price === fruit.price) === index
);
return duplicatedDeleteFruits;
};
Given an array of the following type:
Array<{ foo: T1, bar: T2 }>
You can build a corresponding dictionary of type:
{ [foo: T1]: Set<T2> }
The look-up for { foo: fooValue, bar: barValue } can be performed as follows:
if (fooValue in dictionary && dictionary[fooValue].has(barValue))
This way we can build what would be an ObjectSet<T1, T2>
.
If you now have three elements, you can build the following dictionary:
{ [foo: T1]: ObjectSet<T2, T3> }
and extend your ObjectSet to any number of properties by induction.
That is assuming your types can be used as index signatures.
I have two object literals:
var animal = {
eat: function() {
console.log("eating...");
}
}
var dog = {
eat: "this has to be replaced when merged",
nrOfLegs: 4
}
Need a merging function like this:
dog = someMergingFunction(animal, dog);
That produces:
{
eat: function() {
console.log("eating...");
},
nrOfLegs: 4
}
One of the object literals has to replace identical properties.
How do I do this in Javascript?
The following should work:
function merge(obj1, obj2) {
var obj = {};
for (var x in obj1)
if (obj1.hasOwnProperty(x))
obj[x] = obj1[x];
for (var x in obj2)
if (obj2.hasOwnProperty(x))
obj[x] = obj2[x];
return obj;
}
If both objects have the same property, the value in obj2 takes precedence.
I highly recommend jQuery's extend method, as it will provide a full browser support.
var object = $.extend({}, object1, object2, ..., objectN);
Remember that the first argument is the target. The good point about usage of extend is that by following code, you can make it extend recursively:
var object = $.extend(object, object1, object2, ..., objectN);
See the jQuery's documentation for more info: jQuery Docs for Extend method
// usage merged = someMergingFunction(a, b, c, d, ...)
// keys in earlier args override keys in later args.
// someMergingFunction({foo:"bar"}, {foo:"baz"})
// ==> {foo:"bar"}
function someMergingFunction () {
var o = {}
for (var i = arguments.length - 1; i >= 0; i --) {
var s = arguments[i]
for (var k in s) o[k] = s[k]
}
return o
}
Assume properties of the first parameter will override properties of the 2nd parameter (as your example), this will do:
function merge(obj1, obj2) {
for(attr in obj1)
obj2[attr]=obj1[attr];
return obj2;
}
I recommend using underscore.js as it contains functionality for this and a whole load of related things:
_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
http://underscorejs.org/#extend
As of 2017, I would use Object.assign(foo, bar)
What about spread syntax ?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }
This might be swatting a fly with a buick, but you might be interested to see how Dojo does basically the same thing in dojo.mixin (at least if I understood the question correctly).
https://github.com/dojo/dojo/blob/0dddc5a0bfe3708e4ba829434602da51cbb041b7/_base/_loader/bootstrap.js#L277-366
The basic functionality is in dojo._mixin, while dojo.mixin makes it work iteratively for multiple objects progressively in one shot.
Note that dojo.mixin operates in the opposite direction to what you hinted at in your example.
There are some good suggestions here.
I know this is a really old question, but for future visitors looking for a slightly more flexible solution, I have a similar function that I wrote that accepts any number of objects in an array and merges them all together and returns a single object with the properties of all of the object literals in the array.
Note: the order of precedence is determined by the array. Each subsequent object will overwrite identical properties if they exist in previous objects. Otherwise, new properties are simply added to the single object that is returned.
I hope this will help future visitors to this question. Here's the function, very short and sweet:
var mergeObjects = function (objectsArray) {
var result = {};
for (var i = 0; i < objectsArray.length; i++) {
for (var obj in objectsArray[i]) {
if (objectsArray[i].hasOwnProperty(obj)) {
result[obj] = objectsArray[i][obj];
};
};
};
return result;
};
You can use it like this:
// Define the mergeObjects function
var mergeObjects = function (objectsArray) {
var result = {};
for (var i = 0; i < objectsArray.length; i++) {
for (var obj in objectsArray[i]) {
if (objectsArray[i].hasOwnProperty(obj)) {
result[obj] = objectsArray[i][obj];
};
};
};
return result;
};
// Define some objects to merge, keeping one property consistent so you can
// see it overwrite the old ones
var obj1 = { test1: "test", overwrite: "overwrite1" };
var obj2 = { test2: "test2", overwrite: "overwrite2" };
var obj3 = { test3: "test3", overwrite: "overwrite3" };
// Merge the objects
var newObject = mergeObjects([obj1, obj2, obj3]);
// Test the output
for (var obj in newObject){
if (newObject.hasOwnProperty(obj)){
document.body.innerHTML += newObject[obj] + "<br />";
}
}