Javascript object arrays - how to pass by value but keep name? - javascript

I was coding in javascript and I needed to create an object with multiple sub-objects that would hold the same named variables. When I tried to push values to one sub-object, the others also would also get it. I simulated the behavior in the browser console as following:
object = {};
numbers = [0,1];
letters = ["a","b"]
fruits = ["peach", "pineapple"]
object.subObject1 = { numbers , letters , fruits };
object.subObject2 = { numbers , letters , fruits };
object.subObject1.numbers.push(3);
console.log(object.subObject2.numbers);
OUTPUT: [0, 1, 3]
So, when I push into the array1 sub-object. It also goes to the array2 sub-object, because apparently the numbers array is passed by reference. My question is, how can I do this so that they will keep the sub-object array property name ('numbers') but pass the array by value?

Try this code.
object.subObject1 = { numbers: numbers.slice(), letters: letters.slice(), fruits: fruits.slice() };
object.subObject2 = { numbers: numbers.slice(), letters: letters.slice(), fruits: fruits.slice() };
object.subObject1.numbers.push(3);
console.log(object.subObject2.numbers);
slice apparently invokes that a new array is created for the objects based from the former array, not just passed as the reference.

Related

Manipulating a const array

Why JS allows mutating const arrays?
e.g.
const a = 5
a = 6 //throws error
const arr = [1,2,3]
arr[2] = 4 // no error
Why is it allowed to mutate a const array when it should throw an error as in the first case?
Also how do I ensure that my array remains completely immutable?
It is allowed to add, delete or mutate const array in JS because arr variable stores the memory reference and not the values.
So even if you manipulate the values at that memory location you are not really changing the reference to the memory and hence the variable remains const.
The only thing that is not allowed is re-assigning the array arr = []
which essentially means changing the memory reference the arr variable stores.
As #Nina stated any kind of assignment to const variables is not allowed.
const arr = [1, 2, 3]
arr[2] = 4 //this is fine as memory reference is still the same
arr[3] = 3
console.log(arr)
arr = [] //throws error
console.log(arr)
A scalar value in Javascript is stored by value. For example:
let a = "foo" <--- a is linked to the value "foo"
let b = "foo" <--- b is also linked to the value "foo"
Objects and arrays are stored by reference. For example:
const arr = [1,2,3] <--- arr is linked to a unique address in memory that references 1, 2, and 3
When an array or object is mutated, the address in memory doesn't need to change:
arr.push(4) <-- arr is now [1,2,3,4], but its address in memory doesn't need to change, it's still just an array that references 1, 2, 3, and now 4

make comma separated strings from multidimensional array

I have JavaScript multidimensional array like this:
a 1
b 2
c 3
I want it converted into comma separated value like this:
function reg() {
var result = [];
var comma_value;
for (var i = 0; i < arrc.length; i++) {
if (parseInt(arrc[i].value)) {
result.push([arrp[i].value, arrc[i].value]);
}
// result is an array
// My desired result:
// comma_value = "a,1;b,2;c,3"
}
$('#str').val(JSON.stringify(result));
console.table(result);
console.log(result.join(', '));
}
Given your comments I assume you are simply looking for Array.prototype.join(';'):
let array = [
['a', 1],
['b', 2],
['c', 3]
];
let comma_value = array.join(';');
console.log(comma_value); // 'a,1;b,2;c,3'
This works due to the implicit array element ['a', 1] to string 'a,1' conversion performed within the array.join(';') method call.
If you are curious how this works: When you call array.join(';'), the individual array elements are first converted to strings via the Array.prototype.toString() method:
['a', 1].toString() // returns 'a,1'
Subsequently, those strings are joined with the ';' separator in between.
However, I don't see how this and your comments relate to the given reg() function. That code features two one-dimensional arrays with {value: ...} objects as elements.
alert( [["a",1],["b",2]].map(e=>e.join()).join(";"));
Join the inner Arrays, then the outer...

Javascript - Adding new label and data to existing array

As I know, it is possible to push more data into an array. Fe, I have an array:
G = [12, 34, 5].
Right now, I can access the nth element like this:
G[n]
I'd now like to push new data in it with a label, so I want the array to look like
G = [12, 34, 5, label:567856, other: Infinity]
where I can get 567856 by calling
G["label"] //(or Infinity by calling G["other"]). How can I achieve this?
I've found
G[i].push({
label:567856,
other: Infinity
})
but this way it adds it as a whole new element, and I'm only able to call G[4]["other"], instead of G["other"]. How can I add the element as I've described?
Thank you!
To add onto Andriy's answer, you need to use Javascript Objects rather than arrays. An object can have indices with custom names. For example, given
var newObj = {"hello": "world", "value":1, "inf": Infinity}
you can do
newObj['hello'] // "world"
newObj['value'] // 1
The problem with
G[i].push({
label:567856,
other: Infinity
})
is that you are pushing an object with 2 attributes, not pushing 2 objects, that's why you need to use G[4]["other"]
See running JSFiddle example.
G["other"] = "something";
With this you will keep the original array, and now have the attribute other, but it is not in [12, 34, 5]
Whit this one you can add an object to the array:
G.push({other: 123})
console.log(G);//[12, 34, 5, object]
console.log(G[3].other);//123
The problem with
G[i].push({
label:567856,
other: Infinity
})
is that you are pushing an object with 2 attributes, not pushing 2 objects, that's why you need to use G[4]["other"]
Arrays in JavaScript are a type of object. As such, they can contain properties:
G.label = 567856;
G.other = Infinity;
The advantage of arrays over other objects is that their indexed elements are ordered.
If you'd like the fourth and fifth elements in the array to be 567856 and Infinity and you want to be able to refer to those values with G.label and G.other, you can do so as follows:
var G = [12, 34, 5];
G.push(G.label = 567856); //same as G.label = 567856; G.push(G.label);
G.push(G.other = Infinity);
You can still iterate through the array using a loop:
var G = [12, 34, 5];
G.push(G.label = 567856);
G.push(G.other = Infinity);
G.forEach(function(val) {
console.log(val); // 12 ... 34 ... 5 ... 567856 ... Infinity
});
console.log(G.label); //567856
console.log(G.other); //Infinity
Note that this does create duplicates. If you change G.label or G.other afterwards, those changes will not be reflected in the fourth and fifth elements of the array.
However, you can overcome that by creating setters on G.label and G.other using Object.defineProperty():
var G = [12, 34, 5];
G.push(G.label = 567856);
G.push(G.other = Infinity);
G.forEach(function(val) {
console.log(val); // 12 ... 34 ... 5 ... 567856 ... Infinity
});
console.log(G.label); //567856
console.log(G.other); //Infinity
Object.defineProperty(G, 'label', {
set: function(x) {
this[3] = x;
}
});
Object.defineProperty(G, 'other', {
set: function(x) {
this[4] = x;
}
})
G.label = 99999;
G.other = 11111;
G.forEach(function(val) {
console.log(val); // 12 ... 34 ... 5 ... 99999 ... 11111
});
Arrays isn't designed to suit your case.
See Array element accessing flow from ECMAScript 262, 5.1 15.4
Array objects give special treatment to a certain class of property
names. A property name P (in the form of a String value) is an array
index if and only if ToString(ToUint32(P)) is equal to P and
ToUint32(P) is not equal to 2^32−1.
So you simply cannot access Array element by alphabetical name because that key won't be parsed to integer by ToUint32.
You can add object to array and store it's index after pushing into array ( Array.prototype.push would return you size of your array):
var G = [1,3,4];
var labelIndex = G.push({'label': 123}) - 1;
console.log(G[labelIndex]["label"]);
Actually that's solution would suite case when you have two or more objects inside your array with same property.
Suggestion below not recommended!
However, you can use code below to define your G Array properties, but it's not value of property of item from your array, it's array property:
G.other = Infinity;
G.label = 567856;
// Access newly created properties
console.log(G["other"]);
console.log(G["label"]);
Good Luck !

Should I use curly brackets {} or square brackets [] in this case?

Currently I have an array using an increasing index:
var idx = 1;
var a = [];
a[idx++] = "apple";
a[idx++] = "orange";
...
console.log(a[2]);
And only accessing it by [], not using array specific functions, like length, indexOf, ...
Apparently following is also working in this case:
var a = {};
So, which one should I prefer in such case? For example any performance difference between them?
[ ] denotes an array. Arrays only hold values:
var a1 = [1, 2, 3, 4]
As #Qantas pointed out, array can hold more than just values. An array can even contain another array and/or object:
var a2 = [1, 2, ["apple", "orange"], {one: "grape", two: "banana"}];
{ } denotes an object. Objects have key-value pairs like
var a3 = {one: 1, two: 2}
In your case, it's really a matter of how you would like to be able to access the data. If you are only interested in knowing "apple", "pear", etc. Go ahead and use an array. You can access it via it's index
a1[0]; // outputs 1
a1[1]; // outputs 2
or you can iterate over it with a loop. If you use the curly braces, (given the example I gave) you could access it with
a3.one; // outputs 1
a3["two"]; // outputs 2
It's really up to you on how it would best fit your needs in this case. For a more extensive discussion see this article.
The difference is using square brackets will create an Array object while using curly brackets creates a plain object. For example:
a = [];
a[1] = 'a';
b = {};
b[1] = 'b';
a.length; // returns 2
b.length; // is undefined
a.push('z'); // add 'z' to the end of a
b.push('z'); // generates an error - undefined is not a function
// because plain objects don't have a push method
Read the MDN documentation on Array objects to know more about arrays: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array

How to create an array in JavaScript whose indexing starts at 1?

By default the indexing of every JavaScript array starts from 0. I want to create an array whose indexing starts from 1 instead.
I know, must be very trivial... Thanks for your help.
It isn't trivial. It's impossible. The best you could do is create an object using numeric properties starting at 1 but that's not the same thing.
Why exactly do you want it to start at 1? Either:
Start at 0 and adjust your indices as necessary; or
Start at 0 and just ignore index 0 (ie only use indices 1 and up).
A simple solution is to fill the zeroth item:
var map = [null, 'January', 'February', 'March'];
'First month : ' + map[1];
Semantically it would be better to use an object:
var map = {1:'January', 2:'February', 3:'March'};
'First month : ' + map[1];
Note these keys are not ints actually, object keys are always strings.
Also, we can't use dot notation for accessing. (MDN - Property Accessors)
I'd choose the first solution, which I think is less confusing.
Since this question also pops up for a Google search like "javascript start array at 1" I will give a different answer:
Arrays can be sliced. So you can get a sliced version of the Array like this:
var someArray = [0, 1, 2, 3];
someArray.slice(1);
[1, 2, 3]
someArray.slice(2, 4);
[2, 3]
Source: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
You could use delete to remove the first element like so:
let arr = ['a','b','c'];
delete arr[0];
console.log(arr[0]);
console.log(arr[1]);
Or just not define it at all:
let arr = [,'b','c'];
console.log(arr[0]);
console.log(arr[1]);
If you want to make sure that you always get the first truthy element regardless of the index and have access to ES6 you can use:
arr.find(Boolean)
The question asks "How to create an array in JavaScript whose indexing starts at 1". The accepted answer states "It isn't trivial. It's impossible."
This is true, and should be understood for good reason. However, you can create an array and omit setting the first element, in which case it will still exist (hence the accepted answer being correct) but it'll be marked as empty for you.
let usernames = ['bob', 'sally', 'frank']
let myArray = [];
let arrayIndex = 1;
usernames.map(username => {
myArray[arrayIndex] = username;
arrayIndex++;
})
console.log(myArray);
Array(4) [ <1 empty slot>, "bob", "sally", "frank" ]
1: "bob"
2: "sally"
3: "frank"
​length: 4
Notice that the length is "4".
console.log(myArray[0]);
undefined
Using this, there's a quirk in our favour whereby using Object.keys() on an array doesn't return empty (undefined) elements. So with the above array:
console.log(Object.keys(myArray).length);
3
Note: This is arguably a little hacky so use it with caution.
As zero of something rarely exists in our world, doing this might be useful where you are only going to access pre-defined indexes. An example would be if you have pages of a book. There isn't a page 0 as that makes no sense. And if you are always access a value directly, e.g.
const currentPage = pages[1];
Then this is fine in my opinion, as long as the code shows intent. Some will argue that ignoring a valid array index is futile, and I don't fully disagree. However, it's also futile and very annoying when I want to get page 35 of a book and the array index is 34. Meh!
When you loop your (dodgy) array with map it ignores the 0 index you didn't want:
myArray.map((value, index) => {
console.log(index);
console.log(value);
})
1
bob
2
sally
3
frank
For general use however, you should use index 0, so when you loop some data and spit things out you're not going to get caught out by the first one being empty.
Okay, according to #cletus you couldn't do that because it's a built-in javascript feature but you could go slightly different way if you still want that. You could write your own index-dependent functions of Array (like reduce, map, forEach) to start with 1. It's not a difficult task but still ask yourself: why do I need that?
Array.prototype.mapWithIndexOne = function(func) {
const initial = []
for (let i = 1; i < this.length + 1; i++) {
initial.push(func(this[i - 1], i))
}
return initial
}
const array = ['First', 'Second', 'Third', 'Fourth', 'Fifth']
console.log(array.mapWithIndexOne((element, index) => `${element}-${index}`))
// => ["First-1", "Second-2", "Third-3", "Fourth-4", "Fifth-5"]
Codepen: https://codepen.io/anon/pen/rvbNZR?editors=0012
Using Array.map
[,1,2,3].map((v, i) => ++i)
Just wanted to point out that an index in c ish languages is also the offset from the first element. This allows all sorts of offset math where you don't have to subtract 1 before doing the math, only to add the 1 back later.
if you want a "1" array because the indexes are mapped to other values, that's the case for an enumeration or a hash.
First add this function to your javascript codes:
var oneArray = function(theArray)
{
theArray.splice(0,0,null);
return theArray
}
Now use it like this:
var myArray= oneArray(['My', 'name', 'is', 'Ram']);
alert(myArray[1]); << this line show you: My
See live demo
Just prepend a null:
a = [1, 2, 3, 4]
a.unshift(null)
a[3] // 3
Simple, just make two changes to the classic Javascript for loop.
var Array = ['a', 'b', 'c'];
for (var i = 1; i <= Array.length; i++) {
//"i" starts at 1 and ends
//after it equals "length"
console.log(i);
}

Categories