Mocha, array of array and scope - javascript

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]]);

Related

Return a single object that contains all the fields of the original objects

Write a function that accepts any number of objects, and returns a single object that contains all the fields of the original objects.
If the same key is found in several objects, you should leave the meaning that was first met. My solution does not give the correct result.
function zip() {
const appliedValues = Object.assign({}, ...objects);
return appliedValues;
}
const objects = [
{ foo: 5, bar: 6 },
{ foo: 13, baz: -1 } // foo - repeating key
];
zip(...objects); // I expect { foo: 5, bar: 6, baz: -1 }
Make the record look like this: { foo: 5, bar: 6, baz: -1 }
enter image description here
One very quick change that would achieve this is to simply reverse the passed in array, which will result in the earlier items being used where possible.
To avoid mutating the original array, we can create a copy with [...objects] and then use .reverse() (documentation)
function zip() {
const appliedValues = Object.assign({}, ...[...objects].reverse());
return appliedValues;
}
const objects = [
{ foo: 5, bar: 6 },
{ foo: 13, baz: -1 }
];
console.log(zip(...objects));

Self referencing an Array of JSON to call a function / to update

I'm currently transitioning from VueJS to AlpineJS and trying to update specific JSON arrays but it doesn't seem to be updating.
An example:
Any tips or reasonings why this is not working?
var foo = [
{ id: 0, title: 'test', text: c( this ) },
{ id: 1, title: 'test', text: 'text' },
{ id: 2, title: 'test', text: 'text' },
]
function c( idk ) {
console.log( idk.title )
}
console.log(foo)
var foo = (() => {
// Paste in your original object
const foo = [ {
a: 5,
b: 6,
}
];
// Use their properties
foo.c = foo.a + foo.b;
// Do whatever else you want
// Finally, return object
return foo;
})();
console.log( foo )
These examples were from Self-references in object literals / initializers but has been modified to use an Array of JSON
The first example doesn't work for exactly the same reason as the question you referenced.
The second example doesn't work because foo is the array and not the object inside the array.
foo.a + foo.b is going to be undefined + undefined which you then assign to foo.c making foo.c Not A Number.
You then console.log the array and it doesn't show the c property because it is an array and not designed to have named properties added to it.
var foo = (() => {
const foo = [{
a: 5,
b: 6,
}];
console.log(foo.a, foo.b);
foo.c = foo.a + foo.b;
console.log(foo.c);
return foo;
})();
console.log(foo.c)

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 }

Destructure object property AND whole object itself [duplicate]

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 ]

How do I add the capability to push onto a getter that returns a filtered array?

I have class that has a private array and a getter that returns a filtered version of said array. I would like to be able to simply call a push onto the filtered array which would update the underlying array without having to directly call the underlying array. I've tried accessing the array prototype but couldn't quite figure out how to do so.
Here's a code snippet to emulate what I'm trying to do:
class Test {
_foo: number[];
constructor() {
this._foo = [1,2,3,4,5];
}
get foo(): number[] {
return this._foo.filter(x => {
return x > 2;
});
}
}
let obj = new Test();
obj.foo.push(6);
console.log(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
Any advice is appreciated.
alternative via override:
const {create} = Object;
class Test {
_foo: number[];
_push: Object;
constructor() {
this._foo = [1,2,3,4,5];
this._push = {push: {value: this._foo.push.bind(this._foo)}};
}
get foo(): number[] {
return create(this._foo.filter(x => x > 2), this._push);
}
}
let obj = new Test();
obj.foo.push(6);
console.log(obj.foo.slice()); // 3,4,5,6
You could also use directly:
get foo(): number[] {
const filtered = this._foo.filter(x => x > 2);
filtered.push = this._foo.push.bind(this._foo);
return filtered;
}
or assign this._push once in the constructor as function and do the same, however this might de-optimize in some JS engine (i.e. v8)
You could just have a public method, to do that?
class Test {
_foo: number[];
constructor() {
this._foo = [1,2,3,4,5];
}
get foo(): number[] {
return this._foo.filter(x => {
return x > 2;
});
}
push(x: number) {
this._foo.push(x);
}
}
let obj = new Test();
obj.push(6);
console.log(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
I wouldn't recommend doing this, but if you just need to support push() in a way that updates the backing array (as well as the, uh, "fronting" array), and you don't need to modify things with any other method (such as indexed access), you can do something like this:
class Test {
_foo: number[];
constructor() {
this._foo = [1, 2, 3, 4, 5];
}
get foo(): number[] {
const _foo = this._foo;
const filt: number[] = [];
filt.push = (...args: number[]) => {
_foo.push(...args); // push onto original array
filt.splice(0, filt.length, ..._foo.filter(x => x > 2)); // re-filter
return filt.length;
};
filt.push(); // initialize
return filt;
}
}
Here we are returning a filtered array with a custom push() method that updates itself and the backing array. Here's how it acts:
let obj = new Test();
console.log(obj.foo); // [3, 4, 5]
console.log(obj._foo); // [1, 2, 3, 4, 5]
obj.foo.push(6);
console.log(obj.foo); // [3, 4, 5, 6]
console.log(obj._foo); // [1, 2, 3, 4, 5, 6]
obj.foo.push(9, 10, 11);
console.log(obj.foo); // [3, 4, 5, 6, 9, 10, 11]
console.log(obj._foo); // [1, 2, 3, 4, 5, 6, 9, 10, 11]
obj.foo.push(1, 1000, 0, 2000);
console.log(obj.foo); // [3, 4, 5, 6, 9, 10, 11, 1000, 2000]
console.log(obj._foo); // [1, 2, 3, 4, 5, 6, 9, 10, 11, 1000, 0, 2000]
I... think that's what you want? But really I'd recommend against things with unusual side effects like this. Giving Test a public pushFoo() method seems to be the most conventional approach.
Good luck!
Link to code
You can use a Setter to push into foo
set foo(x) {
this._foo.push(x);
}
class Test {
constructor() {
this._foo = [1, 2, 3, 4, 5];
}
set foo(x) {
this._foo.push(x);
}
get foo() {
return this._foo.filter(x => {
return x > 2;
});
}
}
let obj = new Test();
obj.foo = 6;
console.log(obj.foo);

Categories

Resources