Destructure object property AND whole object itself [duplicate] - javascript

This question already has answers here:
Clean way to keep original variable and destructure at the same time
(5 answers)
Closed 2 years ago.
I have an object. I know I can destructure to retrieve the value of any entry, and use spread operator to retrieve the rest of them
const [a, ...rest] = [1, 2, 3];
console.log(a); // 1
console.log(rest); // [ 2, 3 ]
I would like to know if there is any sintaxis to retrieve both a value of any entry, and the object itself redeclared to a new var, something like the following —although I know is wrong—:
const [a], myArrayInANewVar = [1, 2, 3];
console.log(a); // 1
console.log(myArrayInANewVar); // [ 1, 2, 3 ]
Thanks in advance!

Why not take two assignments?
const myObject = {
a: 1,
b: 2,
};
const
{ a } = myObject,
{ ...copy } = myObject;
console.log(a);
console.log(copy);
A chained assignemnt does not work because the nested variables are created outside of the actual scope.
function ownScope() {
const
myObject = { a: 1, b: 2, },
{ a } = { ...copy } = myObject;
console.log(a);
console.log(copy);
}
ownScope();
console.log(copy); // global

const [a] = (myArrayInANewVar = [1, 2, 3]);
console.log(a); // 1
console.log(myArrayInANewVar); // [ 1, 2, 3 ]

Related

set default values in Object [duplicate]

This question already has answers here:
Update javascript object with another object, but only existing keys
(6 answers)
Closed 10 months ago.
How to achive this result :
const defaultValues = { a:1, b:1 , c:1};// All acceptable properties
const overrideValues = { a:2, x: 3, y: 3, z:3 };// from user
const values = ? ;//
console.log(values);
// output:
// Object { a: 2, b: 1, c: 1 }
Thxs,
Post-Edit :
Thank you all for your precious help.
(I read the duplicate question: Update javascript object with another object, but only existing keys)
However, I still offer you my solution :
const overrideValues = { a:2, x: 3, y: 3, z:3 };
const defaultValues = {
a: overrideValues.a || 1,
b: overrideValues.b || 1,
c: overrideValues.c || 1
};
const values = defaultValues ;// !
console.log(values);
// output:
// Object { a: 2, b: 1, c: 1 }
You can map over the entries of the default values and set the value to the value in overrideValues for each key that exists on that object.
const defaultValues = { a:1, b:1 , c:1};
const overrideValues = { a:2, x: 3, y: 3, z:3 };
const values = Object.fromEntries(Object.entries(defaultValues)
.map(([k,v])=>[k, overrideValues.hasOwnProperty(k) ? overrideValues[k] : v]));
console.log(values);
You can use spread syntax:
const defaultValues = { a:1, b:1 , c:1};// All acceptable values
const overrideValues = { a:2, x: 3, y: 3, z:3 };
const values = {...defaultValues, ...overrideValues};
console.log(values);
Spread syntax allows you to destructure an object or array, and you can use that restructuring to have default values. If there are multiple instances of a key/value pair, then the second value seen for that key is used, allowing you to override the defaults.

TypeScript destructure array of objects, both first object and a value within it

I have an array of objects I want to destructure, retrieving both the first object and a value within it:
const [{ a }] = ([firstObjectInArray] = [
{
a: 1,
},
{
b: 2,
},
]);
console.log(a); // 1
console.log(firstObjectInArray); // { a: 1 }
In Javascript this works; but in TypeScript this returns
Cannot find name 'firstObjectInArray'.ts(2304)
I'm trying to figure out how to type that in order to avoid the error.
As firstObjectInArray isn't part of your declaration (it's just an expression), it's an assignment to an undeclared variable.
To solve the issue, you have two ways:
Do it in two steps:
const [firstObjectInArray] = [
{
a: 1,
},
{
b: 2,
},
];
const {a} = firstObjectInArray
console.log(a); // 1
console.log(firstObjectInArray); // { a: 1 }
Declare the firstObjectInArray earlier:
let firstObjectInArray; //<-- This can't be made `const`, as it has no initializer
const [{ a }] = ([firstObjectInArray] = [
{
a: 1,
},
{
b: 2,
},
]);
console.log(a); // 1
console.log(firstObjectInArray); // { a: 1 }

Using .reduce on an object containing array elements to convert it to array

I'm trying to convert an object which contains arrays as its property, to an array which contains all elements from these nested arrays.
Here's what i am trying to do:
data1 = [{
a: 1,
b: ["uz", "vy"]
}, {
a: 2,
b: ["wxa", "xwy"]
}, {
a: 6,
b: ["ysa", "zaa"]
}]
data1.reduce(function(q, w) {
return q.b.concat(w.b)
})
I want the final data to look like this:
data1 = ["uz","vy","wxa","xwy","ysa","zaa"]
However this gives an error message :
"Uncaught TypeError: Cannot read property 'concat' of undefined"
If q.b refers to array inside object, why does it not have concat property?
What am i doing wrong here?
You are getting an error because q is an array and not an object with b property. And you did not add the initial value.
You can concat and map
let data1 = [{a: 1,b: ["uz", "vy"]}, {a: 2,b: ["wxa", "xwy"]}, {a: 6,b: ["ysa", "zaa"]}];
let result = [].concat(...data1.map(o => o.b));
console.log(result);
If you want to use reduce, you can:
let data1 = [{a: 1,b: ["uz", "vy"]}, {a: 2,b: ["wxa", "xwy"]}, {a: 6,b: ["ysa", "zaa"]}];
let result = data1.reduce(function(q, w) {
return q.concat(w.b);
}, []); //<-- Add [] to initiate the initial value
console.log(result);
Pass an initial value to the reduce function. Also, q.b will not be a valid value in the first call. You wanted to concat to q instead:
data1 = [{
a: 1,
b: ["uz", "vy"]
}, {
a: 2,
b: ["wxa", "xwy"]
}, {
a: 6,
b: ["ysa", "zaa"]
}]
var result = data1.reduce(function(q, w) {
return q.concat(w.b);
}, [])
console.log(result);

Mocha, array of array and scope

I have a code similar to this one:
let Foo = class Foo {
constructor(a) {
this.a = a.slice();
}
bar() {
this.set(0, 1, [1]);
this.set(1, 1, [1]);
}
set(x, y, n) {
this.a[x][y] = n;
}
get() {
return this.a;
}
};
module.exports = Foo;
So a property whis is a array of array, a set function that modify it, and a function that use set, and a getter.
Now I write the unit tests, with mocha:
const Foo = require('./../foo.js'),
source = [[1, 2, 3], [4, 5, 6]],
chai = require('chai'),
expect = chai.expect;
describe('Foo', () => {
describe('bar', () => {
it('expect to modify the array', () => {
let foo = new Foo(source);
foo.bar();
expect(foo.a).to.deep.equal([[1, [1], 3], [4, [1], 6]]);
});
});
describe('get', () => {
it('expect to return the array', () => {
let foo = new Foo(source);
expect(foo.get()).to.deep.equal([1, 2, 3]);
});
});
});
In which I declare the source variable at the beginning, and use it to build a new Foo each test.
However, the result is... that the bar test modify the property of the foo in the get test:
Foo
bar
✓ expect to modify the array
get
1) expect to return the array
1 passing (19ms)
1 failing
1) Foo get expect to return the array:
AssertionError: expected [ [ 1, [ 1 ], 3 ], [ 4, [ 1 ], 6 ] ] to deeply equal [ 1, 2, 3 ]
+ expected - actual
[
- [
- 1
- [
- 1
- ]
- 3
- ]
- [
- 4
- [
- 1
- ]
- 6
- ]
+ 1
+ 2
+ 3
]
at Proxy.assertEqual (node_modules/chai/lib/chai/core/assertions.js:1020:19)
at Proxy.methodWrapper (node_modules/chai/lib/chai/utils/addMethod.js:57:25)
at Context.it (test/foo.js:20:30)
Using anonymous function instead of arrow function in the mocha test doesn't change anything, using or not splice to copy the source by value either.
Did I missed something? It's obvious that I somehow attributed the same reference to the property of both foo to the same array, but I can't figure out how, and more importantly, how to ensure I can create separate the foos.
That is because this.a = a.slice() only takes a shallow copy. But as a is a nested array, changing anything in the nested arrays will be visible in both this.a and source.
You could change:
this.a = a.slice();
to:
this.a = (function deepSlice(a) {
return Array.isArray(a) ? a.map(deepSlice) : a;
})(a);
Note that your second test would need to be changed any way, since you are not comparing to what source was initialised to. So change:
expect(foo.get()).to.deep.equal([1, 2, 3]);
to:
expect(foo.get()).to.deep.equal([[1, 2, 3], [4, 5, 6]]);

javascript merge array of objects, resulting object values in array

Would like to merge an array of objects resulting in an object of unique keys and array of values (duplication of values is ok). Solutions in vanilla JS or lodash preferred.
eg - from this:
[{
a: 1,
b: 2
}, {
a: 1,
c: 3
}]
to this:
{
a: [1, 1],
b: [2],
c: [3]
}
You can use _.mergeWith() with the spread syntax to combine the objects:
const data = [{"a":1,"b":2},{"a":1,"c":3}];
const result = _.mergeWith({}, ...data, (v1 = [], v2) => [...v1, v2]);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
ES6 variant:
const a = [{
a: 1,
b: 2
}, {
a: 1,
c: 3
}]
const b = a.reduce((acc, cur) => Object.assign(acc,
...Object.keys(cur).map(key => ({ [key]: (acc[key] || []).concat(cur[key]) })))
, {})
console.log(b)
without loadash:
var t = [{
a: 1,
b: 2
}, {
a: 1,
c: 3
}];
var result = {};
debugger;
for(var i=0; i<t.length; i++){
for(var j in t[i]){
if(result.hasOwnProperty(j)){
result[j].push(t[i][j]);
}else{
result[j] = [t[i][j]];
}
}
}
console.log(result);
A quick search here in stack reveals that #elclanrs already wrote code for that here However based on the comments, it needs a little tweaking to accept an array of objects, so I added a bit of change to the original code itself.
so basically it boils to the function call:
var merge = function() {
return [].reduce.call(arguments, function(acc, x) {
for(i=0;i<x.length;i++){
Object.keys(x[i]).forEach(function(k) {
acc[k] = (acc[k]||[]).concat([x[i][k]])
});
}
return acc
},{})
}
}
Here's a snippet using the function call (with a bit of small change I put) in that post:
var x = [{a: 1, b: 2}, {a: 1,c: 3}]
var merge = function() {
return [].reduce.call(arguments, function(acc, x) {
for(i=0;i<x.length;i++){
Object.keys(x[i]).forEach(function(k) {
acc[k] = (acc[k]||[]).concat([x[i][k]])
});
}
return acc
},{})
}
y = merge(x);
alert(JSON.stringify(y));
You can use lodash#mergeWith wrapped in a lodash#spread to make lodash#mergeWith treat an array as a list of arguments. We use lodash#concat as a supporting function to concatenate an empty object (to avoid mutating the objects in the collection), the collection, and the customizer function that merges the entire collection. The customizer is composed using lodash#flow, wherein its first argument is lodash#concat that only accepts an arity of 2 using lodash#ary and the second argument uses lodash#compact -- It removes all undefined values in an array.
var result = _.spread(_.mergeWith)(
_.concat({}, data, _.flow(_.ary(_.concat, 2), _.compact))
);
var data = [{
"a": 1,
"b": 2
}, {
"a": 1,
"c": 3
}];
var result = _.spread(_.mergeWith)(
_.concat({}, data, _.flow(_.ary(_.concat, 2), _.compact))
);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Categories

Resources