I was trying to change the structure of a Javascript object and I don't understand the results I am receiving from the logs.
I have the following object: a = {e: 1, f: 2}
And I want to move it to a.b
If I do a.b = a then I receive these results:
console.log(a) // {e: 1, f: 2}
console.log(a.b) // {e: 1, f: 2}
While I am expecting something like this:
console.log(a) // {b: {e: 1, f: 2}}
console.log(a.b) // {e: 1, f: 2}
Can someone explain me why this is happening?
Assigning a value in JS doesn't move it, it copies it.
You are adding a b property to the existing object.
It isn't shown in the log because console.log protects itself against infinite recursion by not displaying the property.
a.b = a simply assigns a.b as a reference to a, which causes a to become a recursive object:
var a = {e: 1, f: 2};
a.b = a;
console.log(a.e, a.f); //1 2
console.log(a.b.e, a.b.f); //1 2
console.log(a.b.b.e, a.b.b.f); //1 2
console.log(a.b.b.b.e, a.b.b.b.f); //1 2
To actually move a's properties into a.b, you'll need to overwrite the existing object, assigning a new property b to its existing value:
var a = {e: 1, f: 2};
a = {b: a};
console.log(a); //{b: {e: 1, f: 2}}
Related
Can someone tell why object merges values but array does not
See the code block below:
const a = {'a': 1, 'b': 2}
const b = {'b': 4, 'c': 3}
console.log({...a, ...b})
This Outputs
{ a: 1, b: 4, c: 3 }
But when I use the code below:
const c = [1,2]
const d = [2,3]
console.log([...c, ...d])
This outputs
[ 1, 2, 2, 3 ]
Why object merges properties...
It doesn't merge properties, it merges objects. Notice the value of b in your result: It's 4 (the value from the b object), not some merged value of 2 (from the a object) and 4 (from the b object). Each property from each source object is just copied into the target object (with later objects overwriting properties from earlier objects), the properties themselves are not merged together.
But fundamentally, object property spread and iterable spread are just completely different things with different purposes and different semantics, because objects and arrays are different beasts (at least conceptually; arrays actually are objects in JavaScript). Properties have names which are an intrinsic part of the property. Array elements just have indexes, and it's normal for values to be moved around an array (moved to different indexes). The two different definitions of spread are each useful for the data type they're defined for.
If you want to treat an array like an object, though, you can since arrays are objects in JavaScript. (Although in this case it isn't useful.) Here's an example (I've changed c's element values so it's clear what's coming from where):
const c = ["a", "b"];
const d = [2, 3];
console.log(Object.assign([], c, d));
In that case, since d has values for both indexes 0 and 1, none of c's elements are in the result. But:
const c = ["a", "b", "c", "d", "e"];
const d = [2, 3];
console.log(Object.assign([], c, d));
Short answer
When using the spread operator, Regular Objects are ASSIGNED.
When using the spread operator, Arrays are CONCATENATED.
Long Answer
I believe the source of your confusion is that every array in JavaScript is just an object belonging to the Array constructor. So why doesn't joining two or more arrays with the spread operator work the same way as objects do?
Let's analyze what is happening in case of the Object
const a = {'a': 1, 'b': 2};
const b = {'b': 4, 'c': 3};
console.log({...a, ...b}); // Output: { a: 1, b: 4, c: 3 }
console.log(Object.assign({}, a, b)); // Output: { a: 1, b: 4, c: 3 }
console.log({...b, ...a}); // Output: { a: 1, b: 2, c: 3 }
console.log(Object.assign({}, b, a)); // Output: { a: 1, b: 2, c: 3 }
An object is a data structure holding key:value pairs.
Object assignment overwrites the keys with the latest values.
The key b occurs in more than one object and is overwritten with it's latest value. As you can see, if you change the order of the objects spread/assigned, the resulting value of the value of b changes based on the latest object having b.
Now let's come to the Array.
const c = [1,2];
const d = [2,3];
console.log([...c, ...d]); // Output: [ 1, 2, 2, 3 ]
console.log(c.concat(d)); // Output: [ 1, 2, 2, 3 ]
console.log(Object.assign({}, c, d)); // Output: { '0': 2, '1': 3 }
console.log(Object.values(Object.assign({}, c, d))); // Output: [ 2, 3 ]
An array is an object created with the Array constructor which outputs the array as a collection of the values assigned to its keys.
Array concatenation simply joins the arrays.
As you can see above, Object.assign still works on an array because the array is technically an object and it behaves exactly how Object.assign is supposed to work. The keys in this case are simply what we call "index" in an array. This is why when you do array[index] it returns the value, it's the same as object[key] that returns a value. If keys exist, the Object.assign replaces the keys/index with the latest values, else it adds the key-value pair to the object.
Conclusion:
Thus, the difference is how the spread operator works for objects and arrays.
In Objects, spread does Object.assign.
In Arrays, spread does Array concatenation => arrayA.concat(arrayB, arrayC, ...)
Bonus: Set
However, if you want the array to return only unique values, you have to use the Set data structure.
const c = [1,2];
const d = [2,3];
console.log([...new Set([...c, ...d])]); // Output: [1, 2, 3]
console.log(Array.from(new Set(a.concat(b)))); // Output: [1, 2, 3]
I have an object which could include a mix of properties e.g.
{a: 1}, {b: 4}, {c: 2}, {a: 3, b: 1}
it will be a key with a count value next to it.
I'd like to set a bunch of variables depending on which key names are in the object, for instance:
aOnly = {a: 1}, mixOfAB = {a: 3, b: 1}
I'm using this logic in a function which will eventually return a string value. What's the best way to operate on this object, I tried using a switch but it didn't work so well. I could use a large number of if/else statements but is there something more neater?
You could do something like this:
vars = {}
function setVariable(obj) {
keys = Object.keys(obj)
name = keys.length == 1 ? keys[0] + 'Only' : 'mixOf' + keys.join('').toUpperCase()
vars[name] = obj
}
setVariable({a: 1})
console.log(vars)
setVariable({b: 4})
console.log(vars)
setVariable({c: 2})
console.log(vars)
setVariable({a: 3, b: 1})
console.log(vars)
This question already has answers here:
One-liner to take some properties from object in ES 6
(12 answers)
Closed 3 years ago.
Is it possible to do this:
const foo = [1, 2];
const bar = {
a: foo[0],
b: foo[1],
c: 'something else not in array'
};
as a single statement, avoiding the need to declare foo?
As example, the array could be the result of
"1, 2".split(', ')
and you want to avoid the interim variable declaration, using "1" and "2" as the values for two of the properties (and potentially not the only properties) in a new object.
I can imagine something like this, though neither is valid for various reasons:
const bar { a, b, c: 'something else not in array' } = [1, 2];
or
const bar { a:[0], b:[1], c: 'something else not in array' } = [1, 2];
EDIT:
The closest I've found, without using an IIFE, is
Object.assign({c: 'something else not in array'}, [1, 2])
which has a negative in that rather than properties named 'a' and 'b', you get properties named '0' and '1':
{0: 1, 1: 2, c: "something else not in array"}
Yes, using an IIFE:
const bar = (([a, b]) => ({ a, b, c: "other props" }))([1, 2]);
If it concerns exactly two properties/values, then reduce can also work:
const bar = [1, 2].reduce((a, b) => ({ a, b, c: "other"}));
When I run the following code:
var object1 = {
a: 1,
b: 2
};
var object2 = {
b: 4,
c: 3
};
var obj1Keys = Object.keys(object1);
var obj2Keys = Object.keys(object2);
console.log(obj2Keys[0] in obj1Keys);
The console logs false. Clearly both object1 and object2 have property 'b'. What am I doing wrong?
You need to use the includes() method instead.
console.log(obj1Keys.includes(obj2Keys[0]));
This is because the in operator works on object keys. The "keys" to an array are number indices (0, 1, etc), not their values
You need to take the object, not the array with the keys for checking and in operator. This check the keys of the object and arrays have indices and length property.
If you like to take the array with the keys, cou could use Array#includes.
var object1 = { a: 1, b: 2 },
object2 = { b: 4, c: 3 },
obj1Keys = Object.keys(object1),
obj2Keys = Object.keys(object2);
console.log(obj2Keys[0] in object1);
console.log(obj1Keys.includes(obj2Keys[0]));
By asking if obj2Keys[0] in obj1Keys, you're asking whether 'b' is a key of ['a', 'b']. It's not. It's a value of ['a', 'b']. You could check like this instead:
obj1Keys.includes(objKeys[0])
Given the following:
var xs = [{a: 1}, {a: 2}, {a: 3}];
R.findIndex(R.propEq('a', 2))(xs); //=> 1
R.findIndex(R.propEq('a', 4))(xs); //=> -1
How do I create a new function that does not bind propEq immediately.
I thought curry might do it.
var myfn = R.findIndex(R.propEq);
myfn('a', '2')(xs); // => 1
I tried curry, but I don't have it quite correct.
var myfn = R.findIndex(R.curry(R.propEq)); // functional programming is rusty - this is not currect
Well, my first thought is that simply being explicit would be best here:
const findByProp = curry((key, val, xs) => findIndex(propEq(key, val), xs));
const xs = [{a: 1, b: 10}, {a: 2, b: 20}, {a: 3, b: 30}];
findByProp('a', 1, xs); //=> 0
findByProp('a', 2)(xs); //=> 1
findByProp('b')(30)(xs); //=> 2
There might be some way to make this points-free using useWith or converge or Sanctuary's S combinator, but they would probably in the end not be as readable as this.
You can see this in action on the Ramda REPL.