Im writing some code atm with ES6 and Babel (es2015 preset) and I cant spread one Object as I am used to. Normally I would take an Object, use a spread and map the inner content like [...someObject].map(dosomestuff). But my one object does not behave as expected and the only difference I found so far are the keys:
let myObject = {
'key': content,
'key2': content,
'key3': content
};
let array = [...myObject];
Since the object gets generated form a file structure, the keys are formed by variables and can include special chars, so i need to set them like object[key] = value. Why cant I use the spread operator on that object (the array is always empty)? And is there a workaround thats as comfortable as the spread-operator (I dont mean making a new Array with Object.keys and use that)
The object spread works on bojects, you can't spread an object into an array.
You should spread it to another object:
let myObject = {
'key': "content1",
'key2': "content2",
'key3': "content3"
};
let myObject2 = {
'key4': 'content4'
}
let newObj= {...myObject, ...myObject2};
console.log(newObj);
This is not part of ES6, but it is on stage 3
Why cant I use the spread operator on that object?
First of all, ... is not an operator!.
You can only use a spread element when that element is iterable. Objects are not iterable.
And is there a workaround thats as comfortable as the spread-operator (I dont mean making a new Array with Object.keys and use that)
Not in ES6.
I am assuming that you want to get the property values from the object.
If you don't wan to write a helper function and don't necessarily want to use a spread element, you can actually make use of Array.from's ability to take a mapping function as second argument:
Array.from(Array.keys(myObject), x => myObject[x]);
If you really don't want to use Object.keys (Object.values doesn't exist in ES6), then you can write yourself your own helper function as a generator:
function* values(obj) {
for (var x in obj) {
// You can use `obj.hasOwnProperty(x)` to guard against inherited properties
// to achieve the same effect as `Object.keys`
yield obj[x];
}
}
let myObject = {
'key': 'content',
'key2': 'content1',
'key3': 'content2'
};
console.log([...values(myObject)]);
// or
console.log(Array.from(values(myObject)));
With Chrome I see that using spread on an object results in an exception. What does work is using Map
Related
I know about the spread operator ... in JavaScript. We can use it on both arrays and objects:
let user = {
name: "aman",
rollno: 11
}
let newobj1 = {...user}
let newobj2 = [...user]
console.log(newobj1)
console.log(newobj2)
Why does newobj2 give an error, TypeError: user is not iterable, but newobj1 works fine?
{...user} just creates a new object with the same properties as user.
[...user], on the other hand, iterates through the object and adds the values it finds into the array it returns. If there is no way to iterate, as there isn't by default on a plain object (as opposed to an array), then this doesn't work and raises the error you've quoted.
As always, there is much more information about this on MDN. Note in particular the following section:
Spread syntax (other than in the case of spread properties) can only
be applied to iterable objects like Array, or with iterating
functions such as map(), reduce(), and assign().
Many objects are not iterable, including Object:
let obj = {'key1': 'value1'};
let array = [...obj]; // TypeError: obj is not iterable
To use spread syntax with these objects, you will need to provide an
iterator function.
And note that the "spread syntax" being referred to here is the "array spread" version. "Object spread" is rather different and explained on this part of the page,
{...user}
means that you create new object and spread all the data from user inside it.
When you suppose to execute
[...user]
you tries to create brand new array and then spread the user object inside.
I'm solving a problem where the task is to merge multiple objects from the input array and return a single object with all the keys and values merged. In case a key exists in one or more objects in the input array, the most recent value for that key should be stored in the final returned object.
An example -
var a={1:'1',2:'2',3:'3'},
b={3:'4',5:'6',6:'7',7:'8'},
c={5:'9',8:'9',6:'12',23:'35'}
o=[a,b,c];
The returned object should be -
{ '1': '1','2': '2','3': '4','5': '9','6': '12','7': '8','8': '9','23':'35' }
As an example of the duplicate key case, key 3 exists in both objects a and b, so in the final result the value is {3:'4'} because that is the most recent.
I want to use the spread syntax for Objects in this problem, but that solves only a part of it. What I mean is, if I use spread for each object individually, that works, like -
function foo() {
var a={'1':'1','2':'2','3':'3'},
b={'3':'4','5':'6','6':'7','7':'8'},
c={'5':'9','8':'9','6':'12','23':'35'},
arr = [a,b,c];
return {...arr[0], ...arr[1], ...arr[2]}
}
console.log(foo());
For this to work, I need to write out each array element with spread, as in the above snippet - ...arr[0], ...arr[1], ...arr[2]. However, the input array can contain any number of objects, so writing out each element is not feasible.
Normally, using spread on an iterable like an array, allows you to expand the array elements, like so-
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
console.log(lyrics)
Is it possible to use spread on the input array to collect all the individual objects, on which spread can be applied again, to get the final object?
You could just use Object.assign():
return Object.assign(...arr);
Here's a complete snippet:
function foo() {
var a={'1':'1','2':'2','3':'3'},
b={'3':'4','5':'6','6':'7','7':'8'},
c={'5':'9','8':'9','6':'12','23':'35'},
arr = [a,b,c];
return Object.assign(...arr);
}
console.log(foo());
Note that this implicitly modifies the first object in your array. If you don't want that, pass a new empty object as the first argument to Object.assign():
return Object.assign({}, ...arr);
You can directly use spread syntax on object to merge them.
function foo() {
var a={'1':'1','2':'2','3':'3'},
b={'3':'4','5':'6','6':'7','7':'8'},
c={'5':'9','8':'9','6':'12','23':'35'};
return {...a, ...b, ...c};
}
console.log(foo());
If you have an array, then you can use array#reduce and Object#assign.
var a={'1':'1','2':'2','3':'3'},
b={'3':'4','5':'6','6':'7','7':'8'},
c={'5':'9','8':'9','6':'12','23':'35'},
arr = [a,b,c];
var result = arr.reduce(function(r,o){
return Object.assign(r,o);
},Object.create(null));
console.log(result);
NOTE : The Rest/Spread Properties for ECMAScript proposal (stage 3) adds spread properties to object literals. It copies own enumerable properties from a provided object onto a new object.
I'm implementing a native module for node and trying to return something that looks like an array but is backed by native data. At the moment I'm creating an ObjectTemplate and using SetIndexedPropertyHandler so I can handle attempts to get indexes from JS. This all works, but from JS this just looks like an Object, not an Array. Is there any way I can make this look more like an array?
You can return Object.entries(object).
The Object.entries() method returns an array of a given
object's own enumerable property [key, value] pairs, in the same
order as that provided by a for...in loop (the difference being that a
for-in loop enumerates properties in the prototype chain as well).
const obj = {a:1, b:2, c:3};
console.log(Object.entries(obj));
All arraylike objects must have length. As long as they have that property, you can borrow Array functions and run them on your object (e.g. Array.prototype.forEach.call(obj, ...)), or in ES6 run Array.from on it:
obj = {
0: "foo",
1: "bar",
length: 2
};
// ES5 way to get a real array:
console.log(Array.prototype.slice.call(obj));
// ES6 way to get a real array:
console.log(Array.from(obj));
If you actually want to return an array and not an arraylike object, then I suggest you use array = Array::New() and array->Set(index, element) instead of ObjectTemplate. There's an example in this question.
You could consider using a Proxy around a real array, with traps/handlers that interface with your native object. There's a performance penalty to using proxies (don't try to iterate over a proxied array if it's an important, hot code path), but they let you do just about anything.
var _arr = [];
var arraylike = new Proxy(_arr, {
get: function (target, prop) {
// Interface with your native object here.
if (prop === "length") return 20;
return "hello";
}
});
// Consume
> arraylike.length
20
> arraylike[5]
"hello"
> arraylike instanceof Array
true
> Array.isArray(arraylike)
true
It's also perfectly valid to prototypically inherit from Array in javascript.
I've read many answers here relating to 'by value' and 'by reference' passing for sending arrays to javascript functions. I am however having a problem sending an array to a function and leaving the original array unaltered. This example llustrates the problem:
function myFunction(someArray)
{
// any function that makes an array based on a passed array;
// someArray has two dimensions;
// I've tried copying the passed array to a new array like this (I've also used 'someArray' directly in the code);
funcArray = new Array();
funcArray = someArray;
var i = 0;
for(i=0; i<funcArray.length; i++)
{
funcArray[i].reverse;
}
return funcArray;
}
I can't understand why anything in this function should alter the original array.
calling this function directly changes the original array if the function call is assigned to a new array:
myArray = [["A","B","C"],["D","E","F"],["G","H","I"]];
anotherArray = new Array();
anotherArray = myFunction(myArray);
// myArray gets modified!;
I tried using .valueOf() to send the primitive:
anotherArray = myFunction(myArray.valueOf());
// myArray gets modified!;
I have even tried breaking the array down element by element and sub-element by sub-element and assigning all to a new 2-d array and the original array still gets modified.
I have also joined the sub-elements to a string, processed them, split them back into arrays and the original array still gets modified.
Please, does any one know how I can pass the array values to a function and not have the passed array change?
Inside your function there's this:
funcArray = new Array();
funcArray = someArray;
This won't actually copy someArray but instead reference it, which is why the original array is modified.
You can use Array.slice() to create a so-called shallow copy of the array.
var funcArray = someArray.slice(0);
Modern versions of ES also support destructuring expressions, which make it look like this:
const funcArray = [...someArray];
The original array will be unaltered, but each of its elements would still reference their corresponding entries in the original array. For "deep cloning" you need to do this recursively; the most efficient way is discussed in the following question:
What is the most efficient way to deep clone an object in JavaScript?
Btw, I've added var before funcArray. Doing so makes it local to the function instead of being a global variable.
Make a copy of the array that you can use.
A simple way to do this is by using var clone = original.slice(0);
With ES6, you can use the rest element syntax (...) within a destructuring expression to perform a shallow copy directly in the parameter list, allowing you to keep the original array unaltered.
See example below:
const arr = [1, 2, 3, 4, 5];
function timesTen([...arr]) { // [...arr] shallow copies the array
for(let i = 0; i < arr.length; i++) {
arr[i] *= 10; // this would usually change the `arr` reference (but in our case doesn't)
}
return arr;
}
console.log(timesTen(arr));
console.log(arr); // unaltered
The above is useful if you have an array of primitives (strings, booleans, numbers, null, undefined, big ints, or symbols) because it does a shallow copy. If you have an array of objects, or an array of arrays (which are also technically just objects), you will want to perform a deep clone to avoid modifying the array and its references. The way to do that in modern-day JS is to use a structured clone, which helps deal with many of the shortcomings of deep cloning with the JSON.stringify() technique as previously used:
function myFunction(someArray) {
const funcArray = structuredClone(someArray);
...
}
What about destructuring assignment (ES6+, check compatibility)? Nice and clean solution.
function myFunction(someArray) {
for(let i = 0; i < someArray.length; i++)
{
someArray[i].reverse();
}
return someArray;
}
let myArray = [["A","B","C"],["D","E","F"],["G","H","I"]];
// Using destructuring assignment.
// NOTE: We can't just use `[...myArray]` because nested arrays will still be copied by reference.
let anotherArray = myFunction([...myArray.map(nested => [...nested])]);
console.log({original: myArray, copy: anotherArray});
A variable pointing to an array is a reference to it. When you pass an array, you're copying this reference.
You can make a shallow copy with slice(). If you want a full depth copy, then recurse in sub objects, keeping in mind the caveats when copying some objects.
If you need to do this with an object, try this fancy trick...
MY_NEW_OBJECT = JSON.parse(JSON.stringify(MY_OBJECT));
A generic solution would be...
// Use the JSON parse to clone the data.
function cloneData(data) {
// Convert the data into a string first
var jsonString = JSON.stringify(data);
// Parse the string to create a new instance of the data
return JSON.parse(jsonString);
}
// An array with data
var original = [1, 2, 3, 4];
function mutate(data) {
// This function changes a value in the array
data[2] = 4;
}
// Mutate clone
mutate(cloneData(original));
// Mutate original
mutate(original);
This works for objects as well as arrays.
Very effective when you need deep cloning or you don't know what the type is.
Deep cloning example...
var arrayWithObjects = [ { id: 1 }, { id: 2 }, { id: 3 } ];
function mutate(data) {
// In this case a property of an object is changed!
data[1].id = 4;
}
// Mutates a (DEEP) cloned version of the array
mutate(cloneData(arrayWithObjects));
console.log(arrayWithObjects[1].id) // ==> 2
Warnings
Using the JSON parser to clone is not the most performant option!
It doesn't clone functions only JSON supported data types
Cannot clone circular references
by default in javascript except objects and arrays, everything is copy-by-value
but if you want to use copy-by-value for arrays: use [yourArray].slice(0)
and for objects use Object.assign(target, ...sources)
var aArray = [0.0, 1.0, 2.0];
var aArrayCopy = aArray.concat();
aArrayCopy[0] = "A changed value.";
console.log("aArray: "+aArray[0]+", "+aArray[1]+", "+aArray[2]);
console.log("aArrayCopy: "+aArrayCopy[0]+", "+aArrayCopy[1]+", "+aArrayCopy[2]);
This answer has been edited. Initially I put forth that the new operator handled the solution, but soon afterward recognized that error. Instead, I opted to use the concat() method to create a copy. The original answer did not show the entire array, so the error was inadvertently concealed. The new output shown below will prove that this answer works as expected.
aArray: 0, 1, 2
aArrayCopy: A changed value., 1, 2
In my script there is a need to create a hash table, and I searched in google for this. Most of the folks are recommending JavaScript object for this purpose. The The problem is some of the keys in the hash table have a "." in them. I am able to create these keys easily with the associative arrays.
I don't understand why associative arrays are bad. The first thing that is mentioned on the sites that I looked at is the length property.
I am coming from the Perl background, where I used hashes. Most common uses were to get the value from a key, check if a key exists, delete a key-value pair, and add a key-value pair. If these are my common uses, can I safely use an associative array?
In JavaScript, objects are associative arrays...there aren't separate concepts for them. You are also able to safely use '.' in a key name, but you can only access the value using the bracket notation:
var foo = {}
foo['bar'] = 'test';
foo['baz.bin'] = 'value';
alert(foo.bar); // Shows 'test'
alert(foo['baz.bin']); // Shows 'value'
If you're using them already and they work, you're safe.
In a JavaScript, an object and array are pretty much the same thing, with an array having a bit of magical functionality (autoupdating the length property and such) and prototype methods suitable for arrays. It is also much easier to construct an object than using an associative array:
var obj = {"my.key": "myValue"};
vs.
var obj = [];
obj["my.key"] = "myValue";
Therefore never use the array object for this, but just the regular object.
Some functionality:
var obj = {}; // Initialized empty object
Delete a key-value pair:
delete obj[key];
Check if a key exists:
key in obj;
Get the key value:
obj[key];
Add a key-value pair:
obj[key] = value;
Because there is no such thing as built-in associative arrays in JavaScript. That's why it's bad.
In fact, when you use something like:
theArray["a"] = "Hello, World!";
It simply creates a property called "a" and set its value to "Hello, World!". This is why the length is always 0, and why the output of alert(theArray) is empty.
Actually, an "associative array" is pretty much the same as an "array-like object" in ECMAScript. Even arrays are objects in ECMAScript, just with the exception to have numeric keys (which are still strings in the background) and a .length property, along with some inherited methods from Array.prototype.
So, a Perl hash and an ECMAScript object behave similarly. You might not know that you can access object properties not only via a dot, but also with brackets and strings, like
var myObj = { foo: 42 };
myObj.foo; // 42
myObj['foo']; // 42
Knowing that, you can also use keys with .
var myObj = { };
myObj['hello.foo.world'] = 42;
Of course, you can access that key only with the bracket notation.
You can use . in key names on JavaScript objects (AKA associative arrays) if you'd like; they're accepted without issue. The minor drawback is you can't use shortcut notations with the dotted keys, e.g.
var x = {};
x['hello'] = 'there';
alert(x.hello);
is perfectly acceptable and will pop up an alert with 'there' in it. But if you use a dotted name:
var x = {};
x['this.is'] = 'sparta';
alert(x.this.is);
will fail, as JavaScript will look for an attribute named this in the x object, which does not exist. There is only the this.is attribute.
There isn't an associative array. It's just an object.
foo.bar; // Equivalent to...
foo["bar"]; // Looks like associative array.
For the sake of convenience of using data, there should be no difference between an object and an array. You can think of it as an object or you can think of it as an associative array. At the end, you can just think of everything as data.
For PHP, [ ] accepts 0, 1, or more items(array), and it is called an associative array. It is JSON in PHP's coat:
$data = ["message"=>[ "id"=>405, "description"=>"Method not allowed.", "detail"=>[]], "object" => []];
For JavaScript, { } accepts 0, 1, or more items(array), and it is called an object. This data format is JSON:
data = {"message": { "id":405, "description":"Method not allowed.", "detail" : {}}, "object" : {}};
I just call them data. The simplest way to describe data is JSON, or its variants.