JavaScript reference drop - javascript

I'm creating a module that extends existing application. I've received a variable device and I want to create myDevice that will always hold the same data. Lets say that data is contained in an array:
https://jsfiddle.net/hmkg9q60/2/
var device = {
name: "one",
data: [1, 2, 3]
};
var myDevice = {
name: "two",
data: []
};
myDevice.data = device.data; // Assign array reference
device.data.push(4); // Push works on array reference
console.log(device.data); // [1, 2, 3, 4]
console.log(myDevice.data); // [1, 2, 3, 4] - ok
device.data = [0, 0, 0]; // A new array is assigned to 'device'
// and 'myDevice' reference stays with old array
console.log(device.data); // [0, 0, 0]
console.log(myDevice.data); // [1, 2, 3, 4] - I would like to get [0,0,0]
What I would like to obtain is to be sure, that myDevice will always hold the same data as device, even if someone decides to use the assign operator somewhere in the application. I don't want to clone myDevice because I want to hold "name" and other properties.
Is there a way in JavaScript to create such reference to an object field?

You could use getters and setters, and read and assign directly from and to the device object.
var device = {
name: "one",
data: [1, 2, 3]
};
var myDevice = {
name: "two",
get data() { return device.data; },
set data(newdata) { device.data = newdata; },
};
console.log(myDevice.data);
device.data = [4,5,6];
console.log(myDevice.data);

I would use a getter
NOTE: more elegant ES6 solution
var device = {
name: "one",
data: [1, 2, 3]
};
var myDevice = {}
myDevice.getData = () => device.data;
myDevice.bla = "bla";
device.data.push(4); // Push works on array reference
console.log(device.data); // [1, 2, 3, 4]
console.log(myDevice.getData()); // [1, 2, 3, 4] - ok
device.data = [0, 0, 0]; // A new array is assigned to 'device'
// and 'myDevice' reference stays with old array
console.log(device.data); // [0, 0, 0]
console.log(myDevice.getData()); // [1, 2, 3, 4] - I would like to get [0,0,0]

Related

Combining JS object keys

I want to combine keys without overwriting the original.
Previously, I had issues with .assign considering some keys are overwritten.
Example of what I'd like:
In:
a = {
first: [1, 2, 3],
second: [5]
}
b = {
first: [5],
second: [6, 7, 8]
}
Out:
{
first: [1, 2, 3, 5],
second: [5, 6, 7, 8]
}
What I've tried:
const c = {...a, ...b}
And:
Object.assign(a, b);
These look to produce similar if not same results.
Lastly:
concat()
But I may have not used the syntax correctly and wasn't able to get a result.
There's no built in assign or spread or so for what you're trying to achieve here.
You have to do it explicitly. Like this, for example:
const a = {
first: [1, 2, 3],
second: [5]
}
const b = {
first: [5],
second: [6, 7, 8]
}
// I want a new Object ...
const out = {
// with a property "first", that consists of
// the items in `a.first` followed by them in `b.first`
first: [...a.first, ...b.first],
// and the same for the property "second"
second: [...a.second, ...b.second],
}
console.log(out);
You might want to create a generic case:
//all objects
const objs = [
{first: [1, 2, 3], second:[5]},
{first: [5], second: [6, 7, 8]},
{first: [3, 4], second: [5, 6]}
]
//unique keys
const unique_keys = new Set([].concat.apply([],objs.map(Object.keys)));
//unified object
const unified_obj = [...unique_keys].reduce((o, key) => {
o[key] = objs.map(e => e[key]).reduce((a, e) => a.concat(e), []);
return o;
}, {});
To get unique keys it's a bit more complex than Object.keys(Object.assign(...objs)), but performs better, as it doesn't deal with values at all.

JavaScript array of objects contains every element of another array

I have an array of objects (array1).
I want to filter every element, that includes ALL of the tags ([1, 2, 3).
So the result should be id 1. But i cannot make it work. The results i get are id 1, 2 ,4 and i do not understand why exactly it acts like this.
let array1 = [
{ id: 1, tags: [1, 2, 3] },
{ id: 2, tags: [2, 3] },
{ id: 3, tags: [0, 3] },
{ id: 4, tags: [1, 3] }
];
let tags = [1, 2, 3];
let includesAll = array1.filter((a1) =>
a1.tags.every((tag) => tags.includes(tag))
);
console.log(includesAll);
You should do the opposite. Instead of verifying that every value in a1.tags is also in tags, you'll want to verify that every value in tags is also in a1.tags:
let includesAll = array1.filter((a1) =>
tags.every((tag) => a1.tags.includes(tag))
);
let array1 = [
{ id: 1, tags: [1, 2, 3] },
{ id: 2, tags: [2, 3] },
{ id: 3, tags: [0, 3] },
{ id: 4, tags: [1, 3] }
];
let tags = [1, 2, 3];
let includesAll = array1.filter((a1) =>
tags.every((tag) => a1.tags.includes(tag))
);
console.log(includesAll);

Extending Array.prototype crashes

I was trying to solve the following problem which I got on a blog but the program crashes. What could be the reason? and is there any means of solving it? I have read warnings not to extend builtin objects, if that's the case, what could be the reason associated with this specific example.
const a = [1, 2, 3, 4, 5];
//this is what I tried
Array.prototype.multiply = function() {
for (m of this) this.push(m * m);
}
a.multiply(); //this should not be changed
console.log(a); // [1, 2, 3, 4, 5, 1, 4, 9, 16, 25] (expected output)
When you push the value to same array during loop, you end up in infinite loop, create a temp array push value to it, in the end add it to this
const a = [1, 2, 3, 4, 5];
//this is the what I tried
Array.prototype.multiply = function() {
let newArr = []
for (const m of this) {
newArr.push(m * m)
}
this.push(...newArr)
}
a.multiply();
console.log(a);
That being said you should not override the prototype simply use a function and pass the parameters
const a = [1, 2, 3, 4, 5];
function multiply(arr) {
return [...arr, ...arr.map(a => a * a)]
}
console.log(multiply(a));
Pushing a new value into an array in the middle of a for ... of loop creates an infinite loop as the loop includes the new values. You can use forEach instead as that ignores the new values added:
const a = [1, 2, 3, 4, 5];
Array.prototype.multiply = function() {
this.forEach(v => this.push(v*v));
}
a.multiply(); //this should not be changed
console.log(a); // [1, 2, 3, 4, 5, 1, 4, 9, 16, 25] (expected output)

How to destructure an array from inside an object in javascript?

I have an object like this:
obj = {'id': 1, a: [1, 2, 3]}
I want to destructure and get the array a from obj
arr = {...obj.a}
I get:
{0: 1, 1: 2, 2: 3}
which is not an array
How to get the array itself ?
You are spreading an array inside {}. This creates an object with indices of the array as keys. This is why you get {0: 1, 1: 2, 2: 3}
const a = [ 1, 2 ]
console.log({ ...a })
If you want to get a property into a variable, this is the correct syntax:
const { propertyName } = yourObject
// if you want to have a variable name which is different than the propertyName
const { propertyName: someOtherVariable } = yourObject
Here's the working snippet:
const obj = {'id': 1, a: [1, 2, 3] }
const { a: arr } = obj; // this is same as: const arr = obj.a
console.log(arr)
Almost - it's the other way round :)
let {a: arr} = {'id': 1, a: [1, 2, 3]}
You could destructure to an array by assigning the array to an array with a rest syntax.
var obj = { id: 1, a: [1, 2, 3] },
[...arr] = obj.a;
console.log(arr);
Use brackets instead of curly braces to spread it into a new array:
const obj = {'id': 1, a: [1, 2, 3]}
const arr = [...obj.a]
console.log(arr)

Convert an Array to unique values only while maintaining the correct sequence

I have the following code:
function uniteUnique(arr) {
//Create a single Array of value
arr = arguments[0].concat(arguments[1], arguments[2]);
//Reduce the Array to unique values only
arr = arr.reduce((pre, curr) => {
//Some function to reduce values
});
return arr;
}
uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
The goal is to produce a single Array containing only unique values while maintaining the order.
Currently it returns:
[1, 3, 2, 5, 2, 1, 4, 2, 1]
I'm wanting to reduce this to:
[1, 3, 2, 5, 4]
You can use Set for that:
function uniteUnique(...args) {
return [...new Set([].concat(...args))];
}
var u = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(u);
It maintains insertion order, and by nature only contains unique values.
In ES5 you could do it by maintaining the used values as properties of a temporary object, while building the result array:
function uniteUnique(/* args */) {
return [].concat.apply([], arguments).reduce(function (acc, v) {
if (!acc[0][v]) acc[0][v] = acc[1].push(v); // assigns new length, i.e. > 0
return acc;
}, [ Object.create(null), [] ])[1];
}
var u = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(u);
You can use the Set object since it already keeps your values unique in one object:
const mySet = new Set([1, 3, 2, 5, 2, 1, 4, 2, 1]);
// returns: Set { 1, 3, 4, 5 };
const arrayUniques = [...mySet];
console.log(arrayUniques);
// returns: [1, 3, 4, 5];

Categories

Resources