const { produce } = require("immer");
const outer = produce((draft) => {
return inner(draft);
}, {});
const inner = produce((draft) => {
draft.arr = [4, 5, 6];
}, {});
outer().arr.sort();
inner().arr.sort();
link: https://codesandbox.io/s/boring-wiles-ezqtr
There is an error on inner().arr.sort(). (read-only error)
My expectation is that outer().arr.sort() also be an error.
Is there something I'm doing wrong here?
Not sure why you want an nested produce but as my understanding you are trying to write a function that leverage immer to sort an array so, to avoid changing initial array.
This is how you could go and from here use another "produce" function that does that. (Code Sandbox)
const { produce } = require("immer")
const baseArray = [6, 10, 3, 2, 1]
const baseArray2 = [17, 9, 10, 3, 2, 1];
function sortedF(state) {
return produce(state, (draft) => {
const sorted = draft.sort((a, b) => a - b)
console.log(sorted);
});
}
const sorted1 = sortedF(baseArray)
console.log(sorted1); // [1, 2, 3, 6, 10]
This use a carried solution curried-produce
const sortedCarried = produce((draft) => {
const sorted2 = draft.sort((a, b) => a - b)
});
const sorted2 = sortedCarried(baseArray2)
console.log(sorted2); // [1, 2, 3, 9, 10, 17]
I have this array of numbers that i am working with that currently look this;
count = [1,4,3,1,2,3,4,5,6,2,3,5,7];
How can i transform and sort it to make it look like this;
count = [1,2,3,4,5,6,7,1,2,3,3,4,5];
Please help, any idea out there on how to approach this?
1) Get unique elements and sort
2) Get remaining elements and sort
3) combine (1) and (2) arrays.
count = [1, 4, 3, 1, 2, 3, 4, 5, 6, 2, 3, 5, 7];
const spSort = arr => {
const uniq = [...new Set([...arr])];
const rem = [];
const temp_set = new Set([...arr]);
arr.forEach(x => {
if (temp_set.has(x)) {
temp_set.delete(x);
} else {
rem.push(x);
}
});
return [...uniq.sort(), ...rem.sort()];
};
console.log(spSort(count));
Use a Set to create unique numbers and a hash object to keep count of duplicates:
const count = [1, 4, 3, 1, 2, 3, 4, 5, 6, 2, 3, 5, 7];
const hash = count.reduce((obj, num) => {
obj[num] = obj[num] ? ++obj[num] : 1;
return obj;
}, {});
const uniq = [...new Set(count)].sort();
uniq.forEach((num, _, arr) => {
while (--hash[num]) arr.push(num);
});
console.info(uniq);
I have an array of objects and I want to find the sum of length of arrays of a certain property(key).
I have this array of objects like
var myArray =
[{
"a" : 1,
"b" : another Array
},
{
"c" : 2,
"b" : another Array
}
.....
]
Is there any way to simplify this below process?
var lengthOfEachObject = myArray.map(function(Obj){
if(Obj.b){
return Obj.b.length;
}
else{
return 0;
}
});
lengthofEachObject.reduce(function(x,y){
return x+y;
})
Answers can also include use of external libraries.
You can use .reduce without .map, this way you can get the total sum by only iterating once over your array. Furthermore, you can use destructing assignment instead of your if statements to set a default value for your b.length if it doesn't exist.
See working example below:
const arr = [{a: 1, b: [1, 2, 3, 4, 5] }, {c: 2, b: [1, 2, 3]}, {e: 3}],
total = arr.reduce((acc, {b={length:0}}) => acc + b.length, 0);
console.log(total);
You can use lodash's _.sumBy():
var myArray = [{"a":1,"b":[1,2,3]},{"c":2,"b":[4,5,6,7,8]},{"c":2}]
var result = _.sumBy(myArray, 'b.length')
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could pull out the length with a closure over the wanted key and map this value.
const
length = k => ({ [k]: { length = 0 } = {} }) => length,
add = (a, b) => a + b,
array = [{ a: 1, b: [1, 2, 3, 4] }, { c: 2, b: [1, 2, 3] }];
console.log(array.map(length('b')).reduce(add));
console.log(array.map(length('a')).reduce(add));
Was just going through the source code for Laravel Mix (webpack setup) to get some inspiration on setting up my own webpack when I came across this.
rules.push(...[].concat(newRules))
I can't figure out what the point of this is but I trust Taylor wouldn't include anything superfluous just for the sake of it.
Surely any of these are just as good?
rules.concat(newRules)
or
rules.push(...newRules)
or even a good old for-loop! But why concat to empty array before spreading the elements?
Much appreciated if anybody can enlighten me on this.
I can only speculate as I didn't author the code but I imagine the intention is to add newRules to rules where newRules could be any type (not just an array). concat will create a new array while we want the original array mutated. push mutates the array but how do you handle the case where newRules is an array? You can't just push newRules into rules because it'll be an array inside an array and you can't spread newRules because not everything is an iterable. [].concat(newRules) will add all of newRules to an array which essentially 'converts' non-arrays into an array and spreading that array inside push will add those items to rules.
Check out the test cases below and click Run snippet to see it in action:
const PASSED = '✅ PASSED';
const FAILED = '❌ FAILED';
(() => {
console.log('`rules.concat(newRules)`');
(() => {
const expectation = [1, 2, 3, 4, 5, 6];
const rules = [1, 2, 3];
const newRules = [4, 5, 6];
rules.concat(newRules);
console.log('where `newRules` is an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
(() => {
const expectation = [1, 2, 3, 4];
const rules = [1, 2, 3];
const newRules = 4;
rules.concat(newRules);
console.log('where `newRules` is not an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
console.log('');
})();
(() => {
console.log('');
console.log('`rules.push(newRules)`');
(() => {
const expectation = [1, 2, 3, 4, 5, 6];
const rules = [1, 2, 3];
const newRules = [4, 5, 6];
rules.push(newRules);
console.log('where `newRules` is an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
(() => {
const expectation = [1, 2, 3, 4];
const rules = [1, 2, 3];
const newRules = 4;
rules.push(newRules);
console.log('where `newRules` is not an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
console.log('');
})();
(() => {
console.log('');
console.log('`rules.push(...[].concat(newRules))`');
(() => {
const expectation = [1, 2, 3, 4, 5, 6];
const rules = [1, 2, 3];
const newRules = [4, 5, 6];
rules.push(...[].concat(newRules));
console.log('where `newRules` is an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
(() => {
const expectation = [1, 2, 3, 4];
const rules = [1, 2, 3];
const newRules = 4;
rules.push(...[].concat(newRules));
console.log('where `newRules` is not an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
})();
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
With Jasmine is there a way to test if 2 arrays contain the same elements, but are not necessarily in the same order? ie
array1 = [1,2,3];
array2 = [3,2,1];
expect(array1).toEqualIgnoreOrder(array2);//should be true
Edit
Jasmine 2.8 adds arrayWithExactContents that will succeed if the actual value is an Array that contains all of the elements in the sample in any order.
See keksmasta's answer
Original (outdated) answer
If it's just integers or other primitive values, you can sort() them before comparing.
expect(array1.sort()).toEqual(array2.sort());
If its objects, combine it with the map() function to extract an identifier that will be compared
array1 = [{id:1}, {id:2}, {id:3}];
array2 = [{id:3}, {id:2}, {id:1}];
expect(array1.map(a => a.id).sort()).toEqual(array2.map(a => a.id).sort());
You could use expect.arrayContaining(array) from standard jest:
const expected = ['Alice', 'Bob'];
it('matches even if received contains additional elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
});
jasmine version 2.8 and later has
jasmine.arrayWithExactContents()
Which expects that an array contains exactly the elements listed, in any order.
array1 = [1,2,3];
array2 = [3,2,1];
expect(array1).toEqual(jasmine.arrayWithExactContents(array2))
See https://jasmine.github.io/api/3.4/jasmine.html
The jest-extended package provides us few assertions to simplify our tests, it's less verbose and for failing tests the error is more explicit.
For this case we could use toIncludeSameMembers
expect([{foo: "bar"}, {baz: "qux"}]).toIncludeSameMembers([{baz: "qux"}, {foo: "bar"}]);
simple...
array1 = [1,2,3];
array2 = [3,2,1];
expect(array1).toEqual(jasmine.arrayContaining(array2));
// check if every element of array2 is element of array1
// to ensure [1, 1] !== [1, 2]
array2.forEach(x => expect(array1).toContain(x))
// check if every element of array1 is element of array2
// to ensure [1, 2] !== [1, 1]
array1.forEach(x => expect(array2).toContain(x))
// check if they have equal length to ensure [1] !== [1, 1]
expect(array1.length).toBe(array2.length)
//Compare arrays without order
//Example
//a1 = [1, 2, 3, 4, 5]
//a2 = [3, 2, 1, 5, 4]
//isEqual(a1, a2) -> true
//a1 = [1, 2, 3, 4, 5];
//a2 = [3, 2, 1, 5, 4, 6];
//isEqual(a1, a2) -> false
function isInArray(a, e) {
for ( var i = a.length; i--; ) {
if ( a[i] === e ) return true;
}
return false;
}
function isEqArrays(a1, a2) {
if ( a1.length !== a2.length ) {
return false;
}
for ( var i = a1.length; i--; ) {
if ( !isInArray( a2, a1[i] ) ) {
return false;
}
}
return true;
}
There is currenly a matcher for this USE CASE:
https://github.com/jest-community/jest-extended/pull/122/files
test('passes when arrays match in a different order', () => {
expect([1, 2, 3]).toMatchArray([3, 1, 2]);
expect([{ foo: 'bar' }, { baz: 'qux' }]).toMatchArray([{ baz: 'qux' }, { foo: 'bar' }]);
});
function equal(arr1, arr2){
return arr1.length === arr2.length
&&
arr1.every((item)=>{
return arr2.indexOf(item) >-1
})
&&
arr2.every((item)=>{
return arr1.indexOf(item) >-1
})
}
The idea here is to first determine if the length of the two arrays are same, then check if all elements are in the other's array.
Here's a solution that will work for any number or arrays
https://gist.github.com/tvler/cc5b2a3f01543e1658b25ca567c078e4
const areUnsortedArraysEqual = (...arrs) =>
arrs.every((arr, i, [first]) => !i || arr.length === first.length) &&
arrs
.map(arr =>
arr.reduce(
(map, item) => map.set(item, (map.get(item) || 0) + 1),
new Map(),
),
)
.every(
(map, i, [first]) =>
!i ||
[...first, ...map].every(([item]) => first.get(item) === map.get(item)),
);
Some tests (a few answers to this question don't account for arrays with multiple items of the same value, so [1, 2, 2] and [1, 2] would incorrectly return true)
[1, 2] true
[1, 2], [1, 2] true
[1, 2], [1, 2], [1, 2] true
[1, 2], [2, 1] true
[1, 1, 2], [1, 2, 1] true
[1, 2], [1, 2, 3] false
[1, 2, 3, 4], [1, 2, 3], [1, 2] false
[1, 2, 2], [1, 2] false
[1, 1, 2], [1, 2, 2] false
[1, 2, 3], [1, 2], [1, 2, 3] false
This algorithm is great for arrays where each item is unique. If not, you can add in something to check for duplicates...
tests = [
[ [1,0,1] , [0,1,1] ],
[ [1,0,1] , [0,0,1] ], //breaks on this one...
[ [2,3,3] , [2,2,3] ], //breaks on this one also...
[ [1,2,3] , [2,1,3] ],
[ [2,3,1] , [1,2,2] ],
[ [2,2,1] , [1,3,2] ]
]
tests.forEach(function(test) {
console.log('eqArraySets( '+test[0]+' , '+test[1]+' ) = '+eqArraySets( test[0] , test[1] ));
});
function eqArraySets(a, b) {
if ( a.length !== b.length ) { return false; }
for ( var i = a.length; i--; ) {
if ( !(b.indexOf(a[i])>-1) ) { return false; }
if ( !(a.indexOf(b[i])>-1) ) { return false; }
}
return true;
}
This approach has worse theoretical worst-case run-time performance, but, because it does not perform any writes on the array, it might be faster in many circumstances (haven't tested performance yet):
WARNING: As Torben pointed out in the comments, this approach only works if both arrays have unique (non-repeating) elements (just like several of the other answers here).
/**
* Determine whether two arrays contain exactly the same elements, independent of order.
* #see https://stackoverflow.com/questions/32103252/expect-arrays-to-be-equal-ignoring-order/48973444#48973444
*/
function cmpIgnoreOrder(a, b) {
const { every, includes } = _;
return a.length === b.length && every(a, v => includes(b, v));
}
// the following should be all true!
const results = [
!!cmpIgnoreOrder([1,2,3], [3,1,2]),
!!cmpIgnoreOrder([4,1,2,3], [3,4,1,2]),
!!cmpIgnoreOrder([], []),
!cmpIgnoreOrder([1,2,3], [3,4,1,2]),
!cmpIgnoreOrder([1], []),
!cmpIgnoreOrder([1, 3, 4], [3,4,5])
];
console.log('Results: ', results)
console.assert(_.reduce(results, (a, b) => a && b, true), 'Test did not pass!');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
I am currently using this helper function (for TypeScript). It makes sure that arrays that have non unique elements are supported as well.
function expectArraysToBeEqualIgnoringOrder<T>(arr1: T[], arr2: T[]) {
while(arr1.length > 0) {
expect(arr1.length).toEqual(arr2.length)
const elementToDelete = arr1[0]
arr1 = arr1.filter(element => element !== elementToDelete)
arr2 = arr2.filter(element => element !== elementToDelete)
}
expect(arr2.length).toEqual(0)
}
Many of the other asnwers do not correctly handle cases like this:
array1: [a, b, b, c]
array2: [a, b, c, c]
Here the number of elements in both arrays is the same and both arrays contain all elements from the other array, yet they are different arrays and the test should fail.
It runs in O(n^2) (precisely (n^2 + n) / 2), so it's not suitable for very large arrays, but it's suitable for arrays that are not easilly sorted and therefore can not be compared in O(n * log(n))