I am creating a multi-dimensional associative array in javascript. I have created a variable inputs with the following structure but when I access it some where else I get undefined. I don't think there is anything wrong with the structure
inputs = {
indexe: {"input_name":$(input).val()},
};
//where indexe is indexe = indexe + 1;
secondly when I try to access inputs[0]["input_name"] I get undefined. Why is that? My main question is that whether the array structure is correct and it should give the values correctly? There is no scope problem here.
I asked you to use console.log() on the input object, because that will make it clear what you problem is:
// your (wrong) way
const indexe = 5;
const yourway = {
indexe: {
"input_name": 'value' // I used a string so I wouldn't have to import jQuery for this
},
};
console.log('Your original version: ', yourway);
// using a real array, this will have a lot of undefined elements
const arrayV = [];
arrayV[indexe] = {
"input_name": 'value'
}
console.log('Array version: ', arrayV);
// using an object correctly
const objectV1 = {
[indexe]: {
"input_name": 'value'
},
};
console.log('Object version: ', objectV1);
// you can add more to it like so:
objectV1[7] = {
"input_name": 'value'
};
console.log('After adding another entry: ', objectV1);
// you also don't need to create a new object with an already existing element. This will suffice:
const objectV2 = {};
objectV2[indexe] = {
"input_name": 'value'
};
console.log('2. Object version: ', objectV2);
// imortant note on the object versions: length is not defined for objects
console.log('Object version 1 length: ', objectV1.length);
console.log('Object version 2 length: ', objectV2.length);
Your problem simply was that it didn't use the value of indexe, but rather that as the name of a property. I have also included the array version, even though that is not an associative array (and as people in the comments have pointed out, neither are the object versions, really).
P.s. I used ES6 Syntax. It is rather widely supported, but I would still recommend going for at least ES5. (There are pre-processors for that)
If you want to make it ES5-valid, replace const with var and don't use the objectV1-version.
Related
I'm looking for a better syntax for writing the following code, and I would like to know if there is an option for assigning the return value of a function by using a destructuring assignment:
const object = {
property: 10,
getFunction() {
return "getFunction value";
}
}
const {property, getFunction} = object;
console.log("Property: ", property, " getFunction: ", getFunction);
Here, this code returns the following, which is totally normal:
"Property: 10, getFunction: [Function: getFunction]"
I'd like to know if there is a syntax option to write something like: (won't work)
const {property, getFunctionValue: getFunction()} = object;
And get the "getFunction value" from the assignment.
Unfortuntely, the syntax you're looking for doesn't exist (I've also wanted to do it many, many times). You can't call a function you're retrieving as part of a destructuring operation.¹ You're not allowed to use an arbitrary expression for the "from" part of a destructuring pattern. Destructuring always does property access, not function calls.
You'll have to do it separately, e.g.:
const { property } = object;
const getFunctionValue = object.getFunction();
or similar.
¹ unless it's the getter function for an accessor property
I basicly need to do some replace logic to change the '.' to a '>' at here everything fine, i can just use the replace method from javascript, but i am searching the best way to do it.
Basicly i will use this function just for 1 specific task nothing more, i want to do this replace logic to my Name propertiy and description inside the object, so instead of doing a simple method that does the replace i need to pass it two times down.
At the moment i have this repeated: element.Name.replace('.', ' > ')
i created a method, but i thaught as the best possible way to maybe pass it to the function like: replaceMethod(firstProp,secondProp) where each prop gets replaced, so how can i inside the replace method just apply the same logic to all my arguments without using a useless for loop?
something like this:
replaceMethod(firstProp,secondProp) {
allArgs.replace('.', ' > ')
}
i did this:
callerFunc() {
// service get the object material, it has a name and description with '.'
replaceMethod(material,material.Name,material.Description)
// do some logic after the method with the material
}
replaceMethod(material,...keys) {
keys.forEach(k => material[k] = material[k].replace(/\./g, ' > '));
}
In ES6, you could use rest parameters ... for collecting all arguments.
function replaceMethod(...keys) {
keys.forEach(k => object[k] = object[k].replace(/\./g, ' > '));
}
var object = { name: 'foo.bar.baz', town: 'st.peter' };
replaceMethod('name', 'town');
console.log(object);
ES5 with use of arguments object.
function replaceMethod() {
Array.prototype.forEach.call(arguments, function (k) {
object[k] = object[k].replace(/\./g, ' > ');
});
}
var object = { name: 'foo.bar.baz', town: 'st.peter' };
replaceMethod('name', 'town');
console.log(object);
I would recommend passing the object as a parameter with the keys of the properties you want to change. Then return a new object with the changes instead of changing the object in place. This is a more functional approach without side effects. You can use the array reduce method. It is most convenient in ES6 using the spread operator ...
function replaceForKeys(obj, ...keys) {
return keys.reduce(
function (result, k) {
return { ...result, [k]: obj[k].replace(/\./g, ' > ') };
},
Object.assign({}, obj)
);
}
var obj = { 'foo': 'foo.bar', 'bar': 'bar.foo' };
var replaced = replaceForKeys(obj, 'foo', 'bar');
So the function takes every argument after the object as an array of keys and reduces over them returning the original object with the property replaced each time. The reduce method takes an initial value as the second parameter and in this case we use Object.assign to use a copy of the original object as the initial value. the [k]: syntax in the object is new in ES6 I believe and is a computed key. It just lets you assign keys in the object without knowing their value beforehand.
Disclaimer:
This is for learning purposes, and I already know adding methods to js built-in objects is not a good practice.
I'm trying to add a new method to the array prototype for a small hobby project:
Array.prototype.add = function (element) {
console.log('array before: ' + JSON.stringify(this));
let arr = this;
if (arr.length) {
let set = new Set(arr);
console.log('set before: ' + JSON.stringify(set));
console.log('adding element: ' + JSON.stringify(element));
set = set.add(element);
console.log('set after: ' + JSON.stringify(set));
} else {
arr.push(element);
}
console.log('array after: ' + JSON.stringify(arr));
};
On attempting to call the new method once it pushes as expected. On a consecutive call the "array before:" log prints as expected with the first push making up the contents of the array, but feeding the array to my Set constructor results in an empty set, as evidenced by my "set before:" and "set after:" logs both printing an empty {}. I'm unsure why the Set won't instantiate from the array, any help would be appreciated.
JSON.stringify always results in an empty object for sets
var s = new Set([1,2,3]);
console.log(s.size);
console.log(JSON.stringify(s));
Sets don't have properties that can be serialized. In other words, you are using the wrong method to verify whether the set is empty and are jumping to the wrong conclusion. Log set.size instead, or just set itself.
Related: JSON stringify a Set
I want to $watch an object for changes on any of its properties and, when any of them changes, get its name (apart from newValue and oldValue).
Is that possible?
This question is two years old but I just ran into the same issue and I thought i'd share my solution to it. Back in the 1.2 version you used to be able to access the changed property by referencing this.exp in the watch function, but that was deprecated presumably for performance reasons. The easiest way to get the changed property is to loop through the new and old objects and compare the values by property name.
$scope.settings = {
'opt1' : 'something',
'opt2' : 'something else',
'opt3' : 'another something',
};
$scope.$watchCollection('settings', function(newObj, oldObj) {
// Loop through new object and compare
angular.forEach(newObj, function(val, key) {
if(newObj[key] !== oldObj[key]) {
// settings[key] changed
}
});
}, true);
No, it's not possible - the only information you get is the new and previous value of an watched object.
You can tell that by looking at the Angular digest loop implementation:
// I ommited most of the code, leaving only the relevant part
if ((watchers = current.$$watchers)) {
while (/* iterate over watchers */) {
// Compare the current value with the last known value
if ((value = watch.get(current)) !== (last = watch.last)) {
// Notify the callback - only the whole values (new and last) are supplied.
watch.fn(value, ((last === initWatchVal) ? value : last), current);
}
}
You could manually enumerate object properties and compare them, or you could use some third party library for that. Quick NPM search returned this: deep-diff (it is available through bower as well). If you do opt to use it, it could look like:
$scope.$watch('watchedObject', function(newValue, oldValue) {
var differences = diff(newValue, oldValue);
// now inspect the differences array to see if there are any
});
Watching an object is not possible, unless you know the variable name in the $scope.
Say you have an object you want to watch at $scope.myobject. You will iterate over the object properties:
var objprops = []
angular.foreach(myobject, function(value, key) {
objprops.push("myobject." + key);
});
objexpr = "[" + objprops.join(",") + "]";
//objexpr will be something like "[myobject.a, myobject.b, myobject.c, ...]"
Then you watch over this array:
$scope.$watch(objexpr, function(newv, oldv){
//do iteration here
});
The big caveat here is that, to detect the changed values, you must iterate the newv array against the oldv array and remember objprops to know which field actually changed.
It's not clean... at all
I have an object in javascript:
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: function() {
return path; // !!! how to reference the 'path'?
}
}
}
I have a lot of objects like this, and each of them has a path field and a link function. I want to use the field path in link, but I can't just use path.
What should I do?
You can use this to reference the object. Standard object.method() "dot" syntax will set this to object within method:
var someObj = {
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: function() {
return this.path; // !!! how to reference the 'path'?
}
}
}
};
var returnedPath = someObj.admins.articles.link();
Demo: http://jsfiddle.net/2Pt7n/
(There are other ways to call a function such that this will not be set to the appropriate object, but I hope they don't apply here - you don't really say how you're using the objects or calling the function, but if not in the way I showed then please update your question and I'll update my answer accordingly.)
I'll just point out that you don't want to use ES6 fat arrow here, because there will be no this pointer in that case:
var someObj = {
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: () => {
return this.path; // 'this' is undefined
}
}
}
};
someObj.admins.articles.link() === undefined
What you are showing is not JSON. It is a Javascript object, which is different than JSON. JSON is a strictly defined data serialization format that is a subset of Javascript object literals.
Javascript provides no syntax for referencing peer properties in an object literal, as you want to do. Naming them is one idea, but it won't help, because the name won't exist while the literal is being defined, so the name is not available for you to use in the literal itself.
Also, note that the syntax you define makes the object lop-sided: you can access path as obj.admins.articles.path, but link is a function you would have to invoke: obj.admins.articles.link().
I won't talk about how this is not JSON (others covered it well).
You can do this to get path:
return admins.articles.path;
Here's a fiddle to show it working: http://jsfiddle.net/UwbLt/
I'm reading the answers and even understanding the point of some users (that JSON should be used just for data) and agreeing that this is correct, I just created a proof of concept example. Take a look.
// just a regular object
var obj = {
a: "aaa",
b: "bbb",
c: function() {
return this.a;
}
};
console.log( obj.c() ); // prints "aaa"
// isn't it json just because it has a function? ExtJS will treat it like JSON, but jQuery not
var json = "{" +
"\"a\": \"aaa\", " +
"\"b\": \"bbb\", " +
"\"c\": function() {" +
" return this.a;" +
"}" +
"}";
// ok, the "json" above
console.log( json );
//var jsonObj = $.parseJSON( json ); // does not work
//var jsonObj = eval( json ); // does not work too
var jsonObj = Ext.decode( json ); // it works! shortcut for Ext.JSON.decode
console.log( jsonObj.c() ); // prints "aaa"
It is almost the same that nnnnnn posted, but I think I would post it too, just to complement the answers. jsFiddle: http://jsfiddle.net/davidbuzatto/rhKAM/
So I think, even contradicting the definition of JSON, that JSON maybe can have (or should have?) the same characteristics of a object created using the regular object initializer sintax, since its name is JavaScript Object Notation, not "Lightweight" Object Notation. I know, I know, a deserializer won't be able to deserialize a function depending on the target language, but why ExtJS supports this "behavior"? A good discussion can be found here: Is it valid to define functions in JSON results?
Just to clarify. I don't use (and I won't use too) functions inside my JSONs.