How to reference an object array item using string? [duplicate] - javascript

This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 5 years ago.
I have an array
fruit={sweet:'apple',dry:{f1:'raisin',f2:'almond'},sour:'strawberry'}
it contains simple and nested objects as items
i can reference f1 using bracket notation like fruit[0]["dry"]["f1"]
but i have a string variable that has the value var str="dry.f1"
value of "str" changes on runtime it could be "sweet" or "dry.f1" or "sour"
how do i reference the array item using "str"
if the value of str is either "sweet" or "sour" fruit[str] works fine
we can get the value of f1 using fruit[0].dry.f1 but i need to access it using the variable str

You can use split and reduce:
var fruit={sweet:'apple',dry:{f1:'raisin',f2:'almond'},sour:'strawberry'};
var str1 = "dry.f1";
var str2 = "sweet";
var example1 = str1.split('.').reduce((a, b) => a[b], fruit);
var example2 = str2.split('.').reduce((a, b) => a[b], fruit);
console.log(example1);
console.log(example2);
This will split your string on each dot into an array, and then reduce the fruit array by iterating through the values from the string, applying them to the fruit array, to get the value you are looking for.

Given array:
fruit={sweet:'apple',dry:{f1:'raisin',f2:'almond'},sour:'strawberry'}
And your string:
var str="dry.f1"
To lookup value fruit.dry.f1 you essentially need to write a parser for "dry.f1"
There are plenty of libraries out there that solve this. I give an example below.
AngularJS
Examples of such parsers exist e.g. angular 1.x's $parse: https://docs.angularjs.org/api/ng/service/$parse
var getter = $parse('dry.f1');
var setter = getter.assign;
var context = {sweet:'apple',dry:{f1:'raisin',f2:'almond'},sour:'strawberry'}
expect(getter(context)).toEqual('raisin');
Lodash
Lodash has a get method: https://lodash.com/docs/4.17.4#get

You could make a conditional, if statement that checks if the string has a dot using str.indexOf('.') and do either
fruit[str]
Or
fruit[str1][str2]

In order to access a value in an object given it's path, we must write a function that searches for that value path inside of the object.
Using split and reduce, we use split to break the path into an array of values that were dot-separated in the path (i.e. "dry.f1" becomes ["dry", "f1"]). We then use reduce to iterate over these values in the array, getting deeper into the object in each iteration until we have our value:
function findValueByPath(obj, path) {
return path.split(".").reduce(function(objSoFar, currPath) {
return objSoFar[currPath];
}, obj);
}
For example, findValueByPath( {a: { b: 5 } } , "a.b") returns 5.
Click here to read more about reduce.
Click here to read more about split.
As a side note, this problem is commonly implemented by libraries such as Lodash, which has the function get that does exactly this (click here for get documentation in Lodash).

Related

Object.keys() array with one element treated as string?

What kind of magic is that, when you Object.keys() on an object with one key only, it will be treated as a string when you reference later using square brackets?
See below for example:
let chosenProducts = [
{
toys: 20
}
];
let toys = "toys";
chosenProducts.forEach(product => {
let name = Object.keys(product);
console.log(Array.isArray(name)); // true
console.log(name) // ['toys']
console.log(toys) // "toys" - string
console.log(product[name]); // 20 - array used = wtf?
console.log(product[toys]); // 20 - string used
});
Object.keys returns an array.
All objects have a toString method.
If you use an object in string context, toString is called implicitly.
Square-bracket property accessor notation is, essentially, string context (unless you pass a Symbol)
The default toString method on an array looks something like: function () { return this.join(","); }
const example1 = ["foo", "bar"];
console.log("" + example1);
const example2 = ["baz"];
console.log("" + example2);
Object.keys returns an array of keys. Your name variable is an array with one element ['toys']. When you access product[name] implicit coercion of name array to string happens which results in product["toys"]
In javascript there are two types of coercions implicit and explicit coercion
When you access with Object's properties they will be string, in case of an array they will be indexes (numbers), but array is also an object, when you add any property in array it can be act as object property(string) and will not get counted towards the length of array.
let arr = [1,3];
arr.val = 20;
console.log(arr.length); // 2
In your example you are accessing object's property product[name] here name is an array. What will happen here is implicit coercion of array to string.
Javascript will automatically call toString method on your object which is an array in this case. There is toString method for Array which is linked to their prototype Array.prototype.toString. You can override that if you want to get different result.
In simple terms when you try to access variable in if conditional expression javascript automatically coerce to Boolean value.
let a = 10;
if(a){
console.log("implicit coercion");
}
if(Boolean(a)){
console.log("explicit coercion");
}
To learn more about Javascript grammar and types.

How can I add an element into an object and how test the existence of a value in a complex object in javascript? [duplicate]

This question already has answers here:
How to use a variable for a key in a JavaScript object literal?
(16 answers)
Closed 4 years ago.
I am new into JavaScript and I have few questions please. First, I am going through two objects to compute the difference between them. So, I have my loop and bunch of if statement for testing. What I am doing is if I find the element I am looking for, I need to add it to a new object while my object is a key and value. The format is like this:
obj={programValue[i] : deviceValue[i]} I do it like that because I want this format : obj={'p1': 'app1'}
This result an error, how can I add an element in my object?
My second question is how can I go through an objects that contains other objects itself and test the existence of a key or a value of another objects that contains objects as well.
Here is an example to explain:
var obj1={{'p1':'app1'}, {'p2':'app2'}};
var obj2= {{'p1':'app2'}, {'p1':'app1'}}
So I want to test if app2 from obj1 exists in obj2, if so then test the key too. What I need is that they have some value (app) but different keys (p1 or p2).
I tried many things but didn't fulfill what I need.
Thank you,
use es6 computed properties
var obj={[programValue[i]] : deviceValue[i]}
or with es5
var obj={};
obj[programValue[i]] = deviceValue[i];
Also for 2nd question:
Object.entries(obj1).some(([key1, value1])=>Object.entries(obj2).some(([key2, value2])=>key1===key2 && value1===value2))
For question 1:
The format is like this: obj={programValue[i] : deviceValue[i]} I do it like that because I want this format : obj={'p1': 'app1'}, This result an error, how can I add an element in my object?
If using ES6, you can do it like this
let obj = {[programValue[i]]:deviceValue[i]};
If programValue[i] returns 'p1' and deviceValue[i] returns 'app1'
then your obj will be {'p1':'app1'}
If you are not using ES6, then you will have to first declare empty object and then set your properties
var obj = {};
obj[[programValue[i]] = deviceValue[i];
For your second question,
var obj2= {{'p1':'app2'}, {'p1':'app1'}}
That structure wont work. It has to be an array of objects. Then you can iterate through your array or directly access the element using index.
var obj2 =[{'p1':'app1'}, {'p2':'app2'}]
obj2[0].p1 // app1
if you still need an object, then you need a property for each of the object
var obj2 = {one: {'p1':'app1}, two: {'p2':'app2}}
obj2.one.p1 //app1
var obj2 = {{'p1':'app2'}, {'p1':'app1'}} is not a valid syntax. The correct object syntax would be var obj2 = {'p1':'app2', 'p2':'app1'}.
To add an element to an object you can do object2['someUniqueId'] = 'whatever you like' or object2.someUniqueId = 'whatever you like'
I believe you have to turn both obj1 and obj2 into an Array: var obj1 = [{'p1':'app1'}, {'p2':'app2'}]; var obj2 = [{'p1':'app1'}, {'p2':'app2'}]; and then you could try to check if an object is in your Array obj2 with this example How to determine if object is in array

Javascript multidimensional arrays with alphanumeric keys

This seems to be a common source of confusion from what I've seen, and apparently I'm no exception. I've read a few tutorials on this, and I still can't quite get my head around it. From what I can gather, Arrays are objects in Javascript, just like Strings and other variable types. But I still don't get how that helps me declare a multidimensional array with alphanumeric keys.
In PHP I can simply write:
$calendar = array();
foreach ($schedule->currentExhibitions as $key) {
$calendar[$key["ExhibitionID"]]["startDate"] = date("Y,n,j", strtotime($exhibition["StartDate"]));
$calendar[$key["ExhibitionID"]]["endDate"] = date("Y,n,j", strtotime($exhibition["StartDate"]));
}
But in Javascript trying something similar will create errors. Should I create an Array and fill it will Objects? If so, how would I go about doing so? Or should I just use an Object entirely and skip having any sort of Array? (If so, how do I create a multidimensional Object?)
Sorry for the newbish quesion!
If your keys are strictly numerical and ordered starting at zero, then an array makes sense and you can use square bracket notation just like you would in php, although you will need to initialize sub-arrays if you want to have multiple dimensions :
var myArray = [];
myArray[0] = [];
myArray[0][0] = "something interesting";
If your keys are not numerical, ordered and starting at zero, then you should use an object (all keys are strings), which still allows the square bracket notation :
var myObject = {};
myObject["1A"] = {};
myObject["1A"]["3B"] = "something interesting";
In Javascript, an array is an object, who's keys are numerical, sequential, indexes.
As soon as you want to use alpha-numerica (aka strings) keys, you use a regular object.
In JS to do what you want, you'd do the following (using more or less your php code).
var calendar = {};
Object.keys(schedule.currentExhibitions).forEach(function(key) {
var ex = schedule.currentExhibitions[key];
calendar[ex.exhibitionId] = calendar[ex.exhibitionId] || {}; //if the key doesn't exist, create it.
calendar[ex.exhibitionId].startDate = date(); //some js date function here
calendar[ex.exhibitionId].endDate = date(); //your js date function here
});
I look at Multidimension as nesting, and at multiple levels of nestings as complex objects. For example:
var parent = [];//top holder
var child1 = {};
child1.name = "Jim";
parent.push(child1);
In this simple example, you can access child1 like this:
parent[0]["name"] //Jim
So that is, in a way, multidemensional. Instead of using ["name"] as an indexer, or child1 as an object it could also be an array, like this:
var parent = [];//top holder
var child1 = [];
child1.push("Jim");
parent.push(child1);
In this example, you could get Jim with:
parent[0][0];//Jim
So for complex examples you may have multiple levels of these nestings (or dimensions).
parent[0]["Child"].grandChild[5]["cousin"].name //etc
Where that would just be a continuation of the previous examples down the line.
If you want to preserve order or you want to access by numeric index, use an array. The value of the array can be a single value or an object or array itself (so each value in the array can contain more than a simple value).
If you want to access by a unique alphanumeric key, then use an object and assign properties to it.
Arrays have numeric indexes. They do not have alphanumeric indexes.
Objects have string keys.
Because an array is also an object, it can have both types of keys, but using a string key is not an array access, it's accessing a property of the object.
When you ask for the .length of an array, you only get the length of the numeric indexes. It does not include other properties of the object.
An array of objects is a very practical data structure in javascript and is used quite often when either order or index by numeric index is important.
If order is not important or you don't need to access by numeric index and just want to access by an alpha numeric string, then you should just use an object and set a properties on it with keys that are your alphanumeric string.

How to merge these arrays/json objects?

I am a bit confused at this point on what is an object, what is an array, and what is a JSON. Can someone explain the differences in syntax between the two? and how to add items to each, how to merge each type, and such? I am trying to get this function to take the new information from a JSON object (I think) and merge it with some new information. This information will then be passed to a PHP script to be processed.
Here is the console output:
{"public":{"0":["el29t7","3bmGDy"]}}
{"public":"[object Object][object Object]"}
Here is the JS I am using:
/* Helper function to clean up any current data we have stored */
function insertSerializedData(ids, type) {
// Get anything in the current field
current_data = $('#changes').val();
if (!current_data) {
var data = {};
data[index++] = ids;
var final_data = {};
final_data[type] = data;
$('#changes').val(JSON.stringify(final_data));
} else {
current_data = JSON.parse(current_data);
var data = {};
data[index++] = ids;
// Does the index exist?
if (type in current_data) {
var temp_data = current_data[type];
current_data[type] = temp_data + data;
} else {
current_data[type] = data;
}
//var extra_data = {};
//extra_data[type] = data;
//$.merge(current_data, extra_data);
$('#changes').val(JSON.stringify(current_data));
}
console.log($('#changes').val());
}
The idea is if the key (public, or whatever other ones) doesn't exist yet, then to make it point to an array of arrays. If it does exist though, then that of array of arrays need to be merged with a new array. For instance:
If I have
{"public":{"0":["el29t7","3bmGDy"]}}
and I want to merge it with
["aj19vA", "jO71Ba"]
then final result would be:
{"public":{"0":["el29t7","3bmGDy"], "1":["aj19vA", "jO71Ba"]}}
How can i go about doing this? Thanks
Excellent two-part question. Overall, the second question is non-trivial because of the complexity of the first.
Question 1:
what is an object, what is an array, and what is a JSON. Can someone
explain the differences in syntax between the two?
Question 2:
and how to add items to each,
Question 3:
how to merge each type, and such?
Answer 1:
This is a common stumbling point because, JavaScript is more flexible than one might initially expect. Here is the curve.
In JavaScript everything is an object.
So here is the code for each:
//What is an object?
var obj = { };
var obj2 = { member:"value", myFunction:function(){} }
Above is an empty object. Then another object with a variable and a function.
They are called object-literals.
//What is an array
var array1 = [ ] ;
var array2 = [0,1,2,3,4];
Above is an empty array. Then another array with five Integers.
Here is the curve that causes confusion.
//Get elements from each of the prior examples.
var x = obj2["member"];
var y = array2[1];
What??? Both Object and Array are accessing values with a bracket?
This is because both are objects. This turns out to be a nice flexibility for writing advanced code. Arrays are objects.
//What is JSON?
JSON stands for JavaScript Object Notiation. As you might have guessed. Everything is an object... It is also an { }; But it is different because - it is used to transfer data to - and - from JavaScript, not actually used (commonly) in JavaScript. It is a file transfer format.
var JSONObject = {"member":"value"};
The only difference to the prior example is quotes. Essentially we are wrapping the object literal as a string so that it can be transferred to a server, or back, and it can be reinterpreted, very easily. Better than XML - because it does not have to be custom-parsed. Just call, stringify() or ParseJSON(). Google it. The point is... JSON can be converted into an object-literal JS object, and JS object-literals can be converted into JSON, for transfer to a server or a CouchDB database, for example.
Sorry for the tangent.
Answer 2:
How to add an item to each? Here is where the curve stops being a nuisance, and starts being awesome! Because everything is an object, it is all just about the same.
//Add to an object
var obj {member1:"stringvalue"}
obj.member2 = "addme"; //That is it!
//Add to an array
var array1 [1,2,3,4,5];
array1[0] = "addme";
array[6] = null;
//We shouldn't mix strings, integers, and nulls in arrays, but this isn't a best-practice tutorial.
Remember the JS object syntax and you may start to see a whole new flexible world of objects open up. But it may take a bit.
Answer 3: Ah, yeah... how to merge.
There are seriously (very many) ways to merge two arrays. It depends on exactly what you need. Sorted, Duplicated, Concatenated... there are a few.
Here is the answer!
UPDATE: How to make a beautiful multiple dimensional array.
//Multiple Dimension Array
var array1 = [1,2,3];
var array2 = [3,4];
var arraysinArray = [array1,array2]; //That is it!
Here is the curve again, this could be in an object:
var obj{
array1:[1,2,3],
array2:[3,4]
}
JavaScript is powerful stuff, stick with it; it gets good. : )
Hope that helps,
All the best!
Nash
In this case, think of a JavaScript's object literal {} as being like PHP's associative array.
Given that, an "array of arrays" actually looks like this (using your above desired output):
{public: [["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"]]}
So here we have an object literal with a single property named "public" whose value is a 2-dimensional array.
If we assign the above to a variable we can then push another array onto "public" like this:
var current_data = {public: [["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"]]};
// Using direct property access
current_data.public.push(["t9t9t9", "r4r4r4"]);
// Or using bracket notation
current_data["public"].push(["w2w2w2", "e0e0e0"]);
current_data's value is now:
{public: [
["el29t7","3bmGDy"],
["aj19vA", "jO71Ba"],
["t9t9t9", "r4r4r4"],
["w2w2w2", "e0e0e0"]
]}
So now "public" is an array whose length is 4.
current_data.public[0]; // ["el29t7","3bmGDy"]
current_data.public[1]; // ["aj19vA", "jO71Ba"]
current_data.public[2]; // ["t9t9t9", "r4r4r4"]
current_data.public[3]; // ["w2w2w2", "e0e0e0"]
MDN has very good documentation on Array for insight on other functions you might need.
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array
First is an object, that contains array, second is an array.
DEMO showing display output http://jsfiddle.net/GjQCV/
var object={"public":{"0":["el29t7","3bmGDy"]}};
var arr=["aj19vA", "jO71Ba"] ;
/* use object notation to add new property and value which is the array*/
object.public[1]=arr;
It'd be much more natural if {"0": ...} were a true array rather than an object, but anyway:
function maxKey(b) {
var max;
for( var key in b )
var max = key;
return max;
}
function merge(a,b) {
for( var key in a ) {
b[key] = b[key] ? (b[key][maxKey(b)+1]=a[key], b[key]) : a[key];
}
return b;
}
Note that this assumes you would insert at the next integer index
Arrays are a particular kind of Javascript object
JSON is a way of representing Javascript objects (and as such can represent arrays and more)
Objects are much more general, and can be simple objects that can be represented as JSON, or can contain functions and prototypes.
So, this is not an array of arrays (you would access items using JSON notation like myobj["0"]):
{"0":["el29t7","3bmGDy"], "1":["aj19vA", "jO71Ba"]}
This is an array of arrays, which means you can use the push method to add an item, and access items using array notation like myobj[0]:
[ ["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"] ]
It seems like the structure you want is something like this:
var myobj = { "public": [ ["key", "value"], ["key", "value"] ] }
Then if you want to add/merge new items, you'd write this:
if (myobj["public"] != null) {
myobj["public"].push(["newkey", "newval"]);
} else {
myobj["public"] = ["newkey", "newval"];
}

Javascript use reserved keyword as key

Doing so
var x = new Array();
x['length']=5;
will make x an array of 5 undefined items, but I actually want to have the value '5' stored at key 'length'.
Is that possible?
In javascript arrays do not have keys. You are looking for objects:
var x = {}
x.length = 5;
I have to parse a file containing many words and store the number of occurences of each word
Use an object, and make the words the keys. You aren't storing sequential / ordered data, so you shouldn't use an array.
var word_count = {};
for (var i; i < words.length; i++) {
var word = words[i];
if (word_count[word]) {
word_count[word]++;
} else {
word_count[word] = 1;
}
If you want to do this you'd be better off creating an object rather than an array. This should give you what you want.
var x = {};
x['length'] = 5;
You can call methods on a javascript object using two different syntaxes. The familiar 'dot' syntax with parens to invoke the method, and the square bracket syntax. You can 'call' a method on a javascript object using the syntax myObj["methodname"](args). This is handy when you want to construct the method name dynamically using strings. Remember, objects in javascript are very much like a hash table (dictionary) where keys denote property and function names. If a key's value holds a function, it can be invoked (using parentheses).
In your example, Array has a method called 'length'. You are inadvertently calling its setter (which sets the length of the array to empty values, i.e., undefined).
Putting that all aside, you really do want a hash (associative array) in this case. An array is an offset indexed data structure.
A simple object literal like myObj = {} will suffice to give you hash semantics (again, objects in javascript are already like hashes) and you can then call myObj.whatever = "some value"
You could use objects instead of arrays to store your data. But if you need to use Arrays (you might need to use their functionality), You could cripple the words and store them as array keys.
Use some kind of simple rule to follow to bypass all the possible keywords. For example prefix all your array keys with a "_" character. This way you could always restore the original words from the keys, by simply removing their first character, and you are sure you are not referencing any specific property of the Array objects (like the length property).

Categories