How to merge these arrays/json objects? - javascript

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"];
}

Related

Copying an array of objects into another array in javascript (Deep Copy)

Copying an array of objects into another array in javascript using slice(0) and concat() doesnt work.
I have tried the following to test if i get the expected behaviour of deep copy using this. But the original array is also getting modified after i make changes in the copied array.
var tags = [];
for(var i=0; i<3; i++) {
tags.push({
sortOrder: i,
type: 'miss'
})
}
for(var tag in tags) {
if(tags[tag].sortOrder == 1) {
tags[tag].type = 'done'
}
}
console.dir(tags)
var copy = tags.slice(0)
console.dir(copy)
copy[0].type = 'test'
console.dir(tags)
var another = tags.concat()
another[0].type = 'miss'
console.dir(tags)
How can i do a deep copy of a array into another, so that the original array is not modified if i make a change in copy array.
Try
var copy = JSON.parse(JSON.stringify(tags));
Try the following
// Deep copy
var newArray = jQuery.extend(true, [], oldArray);
For more details check this question out What is the most efficient way to deep clone an object in JavaScript?
As mentioned Here .slice(0) will be effective in cloning the array with primitive type elements. However in your example tags array contains anonymous objects. Hence any changes to these objects in cloned array are reflected in tags array.
#dangh's reply above derefences these element objects and create new ones.
Here is another thread addressing similar situation
A nice way to clone an array of objects with ES6 is to use spread syntax:
const clonedArray = [...oldArray];
MDN
Easiest and the optimistic way of doing this in one single line is using Underscore/Lodash
let a = _.map(b, _.clone)
You just need to use the '...' notation.
// THE FOLLOWING LINE COPIES all elements of 'tags' INTO 'copy'
var copy = [...tags]
When you have an array say x, [...x] creates a new array with all the values of x. Be careful because this notation works slightly differently on objects. It splits the objects into all of its key, value pairs. So if you want to pass all the key value pairs of an object into a function you just need to pass function({...obj})
Same issue happen to me. I have data from service and save to another variable. When ever I update my array the copied array also updated. old code is like below
//$scope.MyData get from service
$scope.MyDataOriginal = $scope.MyData;
So when ever I change $scope.MyData also change $scope.MyDataOriginal.
I found a solution that angular.copy right code as below
$scope.MyDataOriginal = angular.copy($scope.MyData);
I know that this is a bit older post but I had the good fortune to have found a decent way to deep copy arrays, even those containing arrays, and objects, and even objects containing arrays are copied... I only can see one issue with this code is if you don't have enough memory I can see this choking on very large arrays of arrays and objects... But for the most part it should work. The reason that I am posting this here is that it accomplishes the OP request to copy array of objects by value and not by reference... so now with the code (the checks are from SO, the main copy function I wrote myself, not that some one else probably hasn't written before, I am just not aware of them)::
var isArray = function(a){return (!!a) && (a.constructor===Array);}
var isObject = function(a){return (!!a) && (a.constructor===Object);}
Array.prototype.copy = function(){
var newvals=[],
self=this;
for(var i = 0;i < self.length;i++){
var e=self[i];
if(isObject(e)){
var tmp={},
oKeys=Object.keys(e);
for(var x = 0;x < oKeys.length;x++){
var oks=oKeys[x];
if(isArray(e[oks])){
tmp[oks]=e[oks].copy();
} else {
tmp[oks]=e[oks];
}
}
newvals.push(tmp);
} else {
if(isArray(e)){
newvals.push(e.copy());
} else {
newvals.push(e);
}
}
}
return newvals;
}
This function (Array.prototype.copy) uses recursion to recall it self when an object or array is called returning the values as needed. The process is decently speedy, and does exactly what you would want it to do, it does a deep copy of an array, by value... Tested in chrome, and IE11 and it works in these two browsers.
The way to deeply copy an array in JavaScript with JSON.parse:
let orginalArray=
[
{firstName:"Choton", lastName:"Mohammad", age:26},
{firstName:"Mohammad", lastName:"Ishaque", age:26}
];
let copyArray = JSON.parse(JSON.stringify(orginalArray));
copyArray[0].age=27;
console.log("copyArray",copyArray);
console.log("orginalArray",orginalArray);
For this i use the new ECMAScript 6 Object.assign method :
let oldObject = [1,3,5,"test"];
let newObject = Object.assign({}, oldObject)
the first argument of this method is the array to be updated,
we pass an empty object because we want to have a completely new object,
also you can add other objects to be copied too :
let newObject = Object.assign({}, oldObject, o2, o3, ...)

Adding fields to JS array

I've added a field "max" to the JS array prototype. I'm having trouble seeing this addition when I stringify the array. I've tried to overwrite the toJSON function but to no avail.
Array.prototype.max = 0;
Array.prototype.toJSON = function(){
var o = this;
o.max = this.max;
return o;
};
var a = [1,2,3];
a.max = 10;
console.log(JSON.stringify(a));
//'[1,2,3]'
I'd like to avoid doing something like {array_field:a,max_field:max} and just use the array object - is this possible? What am I doing wrong?
You have a few options here, none of them will do exactly what you want.
Since JSON arrays are strictly limited to integer keys[1], you can't send down the value of max directly.
Your choices include:
Using an object instead of an array. You loose all of the array magic, but it might work better for you.
Send down meta-data outside your array, such as a configuration object that includes both max and your array values
Force the array to be max long before sending it down. JSON will encode unused array elements with null.
Use a custom serialzer/deserializer. You could even code things in JSONS + the extras. In reality, this pretty much turns out the same as the second option.
[1] Unlike JavaScript Objects

Javascript: Change name of an array into a string

Below, I have an array of arrays of objects. I go through looking for my object, and once I find which array it's in, I want to get at and work with that array's name as a string. My guess, was something like Array.name (as it plays out below), but that doesn't work.
ActiveDocument.gaShapesTab1 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape2"]);
ActiveDocument.gaShapesTab2 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape4"]);
ActiveDocument.gaShapesTab3 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape6"]);
ActiveDocument.gaShapeArrays = new Array(gaShapesTab1, gaShapesTab2, gaShapesTab3);
// go through an array of arrays
for(var x=0; x<gaShapeArrays.length; x++)
{
// and go through the objects of each one
for(var y=0; y<gaShapeArrays[x].length; y++)
{
// if "object" is in the array
if(object == gaShapeArrays[x][y])
{
// get "sidetab" from object's array's name
var sidetab = gaShapeArrays[x].name.replace('gaShapes',''); // assumes that shapearrays will have naming convention gaShapesSidetab
// we found it, we can stop now
break;
}
}
}
I'm working in Hyperion Intelligence, so not all Javascript will apply. For instance I don't have access to window or document.
Each array contains a set of shape objects related to a visual tab. This allows me to show or hide or do more complex operation with what's on each tab simply by calling the array of shapes. But, when working with the shapes, themselves, I need to know which tab they're on. I'm trying to work backwards by finding which array they're in.
You don't want to do that.
If you really need to find a value in several arrays and then pull out an identifier, then you want a dictionary, not named variables:
var dictOfArrays = {
'evens': [0,2,4,6,8,10],
'odds': [1,3,5,7,9]
};
This stores the identifier that you seek as data, so you can store that identifier and use it later to retrieve the value if you want:
var whichArrayKey = findMyValuesKey(value, dictOfArrays);
console.log('Value '+value+' is in array keyed '+whichArrayKey);
var matchingArray = dictOfArrays[whichArrayKey];
var firstValueInMatchingArray = matchingArray[0];
The name of a variable is just something for you, the developer, to use to know which thing is which. It's just a handle for a place in memory where stuff is stored. As such, it doesn't mean anything to the code. If you actually want to use it in the program, then it is data, not code, and should be encoded in a data structure like a dictionary as above. That way you can pass the array or the identifier around as much as you please, and the behaviour of the code doesn't have to be tied to the names you give your variables.
Edit 1:
The newly added code, in dictionary form/object notation:
ActiveDocument.gaShapeArrays = {
'gaShapesTab1' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape2"]
],
'gaShapesTab2' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape4"]
],
'gaShapesTab3' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape6"]
]
}
So each key (e.g. 'gaShapesTab1') is paired with an array value ([...]). This is instead of using new Array() everywhere.
Once you have found the key of the array containing a reference matching your object, you'll have that key as a string (e.g. "gaShapesTab3"). You can't change this string in-place, and I don't think you'd want to. If you could clarify why you need to change the name of the array, perhaps it will be clear how to resolve the problem. For example, do you have other code that needs the array to have a particular name?
Array's name? Arrays do not have names. You only have variable names, variables that store your arrays. If you have a two-dimensional array, you need to grab the "coordinates".
So:
if(object == gaShapeArrays[x][y])
{
// Found the object! It's in [x][y], so in array gaShapeArrays[x] which
// itself is in gaShapeArrays
}
Even though I think #Phil H gave me the answer to my question, as the proper way to do it, I have other reasons to do it the way #ben336 was commenting. It might not be proper, but I'm posting what the solution was in the end. Fortunately, I already had the gaSidetabs array elsewhere in my startup script for another function. I just assigned a string value to the .name property of each array. Would've been nice to know if there was a way to "get at" the symbolic name (or whatever you want to call it) that I called the array, but it sounds like that's just not possible.
ActiveDocument.gaShapesTab1 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],ActiveDocument.Sections["Dashboard"].Shapes["Shape2"]);
ActiveDocument.gaShapesTab2 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],ActiveDocument.Sections["Dashboard"].Shapes["Shape4"]);
ActiveDocument.gaShapesTab3 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],ActiveDocument.Sections["Dashboard"].Shapes["Shape6"]);
ActiveDocument.gaShapeArrays = new Array(gaShapesTab1, gaShapesTab2, gaShapesTab3);
ActiveDocument.gaSidetabs = new Array('Tab1','Tab2','Tab3');
// Assigns a .name javascript property to each array. assumes the order and length of the arrays is the same.
if (gaShapeArrays.length == gaSidetabs.length)
{
for (var x = 0; x < gaShapeArrays.length; x++)
{
gaShapeArrays[x].name = gaSidetabs[x];
}
}
else
{
Console.Writeln('Warning: gaShapeArrays and gaSidetabs are not the same length. Some names will not be assigned.');
}
// go through an array of arrays
for(var x=0; x<gaShapeArrays.length; x++)
{
// and go through the objects of each one
for(var y=0; y<gaShapeArrays[x].length; y++)
{
// if "object" is in the array
if(object == gaShapeArrays[x][y])
{
// get "sidetab" from object's array's name
var sidetab = gaShapeArrays[x].name.replace('gaShapes',''); // assumes that shapearrays will have naming convention gaShapesSidetab
// we found it, we can stop now
break;
}
}
}
Alert(sidetab);
Also glad I could figure out how to retain the format of the code block, here.

Initialize an array whose indexes are scattered over integer range

How to initialize a string array (size<100 items) in javascript whose indexes are scattered over entire integer range, with data items.
If I do like this:
array1 = ["string1","string2","string3","string4"];
then I get array of length 4 with indices ranging 0 to 3
But in my case i want to keep my own indices, so that the array could be used like a high performance int-string hash table.
I'm preferably looking out for a single statement initialization.
The items of the array should be accessible like this: array1[23454]
Update From Comments
I'm restricted to initialize the array as a single statement since a dynamically prepared array initialization string is appended from server side like this: var array = <string from server here>
To create an array with a set number of indexes you can use
// Creates an array with 12 indexes
var myArray = new Array(12);
This isn't needed in javascript due to the way its array's work. There isn't an upper-bound for arrays. If you try to reference an item index in the array that doesn't exist, undefined is returned but no error is thrown
To create an array with perscribed indexes you can use something like array['index'] = value though this would force you to use multiple statements. Javascript doesn't have an array initalizer to allow for you to specify indexes and values all in a single statement though you can create a function to do as such
function indexArray(param) {
var a = [], i;
for (i=0; i<param.length; i+=1) {
a[param[i].index] = param[i].value;
}
return a;
}
var myArray = indexArray([
{ index: 123456, value : "bananas" },
{ index: 12, value : "grapes" },
{ index: 564, value : "monkeys" }
]);
var array1 = []
array1[23454] = 2
Just doing this should be fine. There's no set array size for javascript in the way there is for java.
If you really want to do this all in a single statement, you can make an object instead like this:
var object1 = {
"23454":2,
"123":1,
"50":3
};
and then retrieve the numbers like this:
object1["23454"] //2
I don't really recommend this though. The array method is a cleaner way of doing it even if it takes multiple lines since it doesn't require string conversion. I don't know enough about how these are implemented in browsers to comment on the performance impact.
Update
Since the 1 line requirement is based on something being passed to the server, I would recommend passing a JSON object to the server in the form:
"{"23454":2,"123":1,"50":3}"
then this code will parse it to an object:
var object1 = JSON.parse(jsonstringfromserver);
and if you like you can always convert that to an array by enumerating over the properties with a for in loop:
var array1 = []
for ( num in object1){
array1[num] = object1[num];
That is probably unnecessary though since object1[123] will already return 1. You only need this if you plan on doing array specific operations.
You don't have to pre-define the size of an array before you assign to it. For example:
var _array = [];
_array[0] = "foo";
_array[1000] = "bar"; // _array.length => 1001
_array[1] //undefined
No need to initialise the appropriate number of array elements before you assign to them.
Update
It already has been pointed out that you can use an object rather than an array. However, if you want to take advantage of array methods then this is still possible. Let me give you an example:
var obj = {
0: 15,
1: 10,
2: 5,
length: 3
};
If the object contains a length property then it can be treated as an array-like object. Although you can't call array methods directly from these objects you can use array methods.
Array.prototype.join.call( obj ); // 15,10,5
In fact using the ECMAScript 5 map function you can easily convert the above object to an array.
var _array = Array.prototype.map.call( obj, function( x ) { return x; } );
The map function does not exist in all browsers but you can use the following function if it doesn't.
Array.map = Array.map || function(a, f, thisArg) {
return Array.prototype.map.call(a, f, thisArg);
}
You can do what you want with an Object in this way:
var o = {23454: 'aaaa', 23473: 'bbb'};
You will lose the array methods/fields, e.g. length, but you will gain what you said you are looking for, and you will be able to add/remove members easily.

Creating multi-dimensional arrays in javascript, error in custom function

I was trying to define an array (including other arrays as values) in a single javascript statement, that I can loop through to validate a form on submission.
The function I wrote to (try to) create inline arrays follows:
function arr(){
var inc;
var tempa = new Array(Math.round(arguments.length/2));
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
This is called three times here to assign an array:
window.validArr = arr(
'f-county',arr('maxlen',10, 'minlen',1),
'f-postcode',arr('maxlen',8, 'minlen',6)
);
However in the javascript debugger the variable is empty, and the arr() function is not returning anything. Does anyone know why my expectations on what this code should do are incorrect?
(I have worked out how to create the array without this function, but I'm curious why this code doesn't work (I thought I understood javascript better than this).)
Well from what your code does, you're not really making arrays. In JavaScript, the thing that makes arrays special is the management of the numerically indexed properties. Otherwise they're just objects, so they can have other properties too, but if you're not using arrays as arrays you might as well just use objects:
function arr(){
var inc;
var tempa = {};
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
What you're seeing from the debugger is the result of it attempting to show you your array as a real array should be shown: that is, its numerically indexed properties. If you call your "arr()" function as is and then look at (from your example) the "f-county" property of the result, you'll see something there.
Also, if you do find yourself wanting a real array, there's absolutely no point in initializing them to a particular size. Just create a new array with []:
var tempa = [];
Your code works. Just inspect your variable, and you will see that the array has the custom keys on it. If not expanded, your debugger shows you just the (numerical) indixed values in short syntax - none for you.
But, you may need to understand the difference between Arrays and Objects. An Object is just key-value-pairs (you could call it a "map"), and its prototype. An Array is a special type of object. It has special prototype methods, a length functionality and a different approach: to store index-value-pairs (even though indexes are still keys). So, you shouldn't use an Array as an associative array.
Therefore, their literal syntax differs:
var array = ["indexed with key 0", "indexed with key 1", ...];
var object = {"custom":"keyed as 'custom'", "another":"string", ...};
// but you still can add keys to array objects:
array.custom = "keyed as 'custom'";

Categories