I'm using the matrix element from the math.js library.
I create a matrix:
let eye = math.matrix([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
]);
But when I try to access an element of the matrix, it is always undefined.
console.log(eye[0][0]) -> undefined
console.log(eye[0]) -> undefined
Any suggestions? I have read through the documentation for the math.js library and I see nothing about how to access an individual element.
And I do need to use this library, as I am doing matrix-based operations (matrix multiplication).
If you print out eye, you get the following object.
{
_mathjs: "DenseMatrix",
_data: [
[ 1, 0, 0 ],
[ 0, 1, 0 ],
[ 0, 0, 1 ]
],
_size: [ 3, 3 ]
}
But that is just the internal structure used to hold information about the matrix, you need to call helper methods as seen in the docs.
https://mathjs.org/docs/reference/functions/subset.html
https://mathjs.org/examples/matrices.js.html
If you want to access the data, you can always interrogate the internal matrix data directly. This is a bad idea, because the fields are all treated as private; internal fields (start with an underscore).
const __print = (x) => console.log(JSON.stringify(x));
let eye = math.matrix([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
]);
__print(eye._data[0][0]); // 1
__print(eye._data[0]); // [1, 0, 0]
__print(eye._data[1]); // [0, 1, 0]
__print(eye._data[2]); // [0, 0, 1]
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/8.1.1/math.js"></script>
It is recommended that you use the helper methods which are available in the library.
Note: After fine-tuning my response, I found a similar method of retrieving a row in the Issue #230 on GitHub.
You need to figure out the number of columns in the matrix. After that, you can grab a subset starting at the desired row index with a range of 0 to the number of columns in the row. The result must then be flattened.
const __print = (x) => console.log(JSON.stringify(x));
let eye = math.matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const __row = (m, r) =>
(([ rows, cols ]) => math.flatten(math.subset(m,
math.index(r, math.range(0, cols))).valueOf()))
(m.size());
const __col = (m, c) =>
(([ rows, cols ]) => math.flatten(math.subset(m,
math.index(math.range(0, rows), c)).valueOf()))
(m.size());
const __cell = (m, r, c) =>
math.subset(m, math.index(r, c));
__print(__cell(eye, 0, 0)); // 1
__print(__row(eye, 0)); // [1, 2, 3]
__print(__row(eye, 1)); // [4, 5, 6]
__print(__row(eye, 2)); // [7, 8, 9]
__print(__col(eye, 0)); // [1, 4, 7]
__print(__col(eye, 1)); // [2, 5, 8]
__print(__col(eye, 2)); // [3, 6, 9]
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/8.1.1/math.js"></script>
Turns out that they are already implemented, but you still need to flatten the result.
const __print = (x) => console.log(JSON.stringify(x));
let eye = math.matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
const __row = (m, r) =>
math.flatten(math.row(m, r).valueOf());
const __col = (m, c) =>
math.flatten(math.column(m, c).valueOf());
const __cell = (m, r, c) =>
math.subset(m, math.index(r, c));
__print(__cell(eye, 0, 0)); // 1
__print(__row(eye, 0)); // [1, 2, 3]
__print(__row(eye, 1)); // [4, 5, 6]
__print(__row(eye, 2)); // [7, 8, 9]
__print(__col(eye, 0)); // [1, 4, 7]
__print(__col(eye, 1)); // [2, 5, 8]
__print(__col(eye, 2)); // [3, 6, 9]
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/8.1.1/math.js"></script>
You might want to try to combine both subset and index to get the value at the index.
Source: mathjs
math.subset(eye, math.index(0, 0)) // 1
math.subset(eye, math.index(0, 1)) // 0
You are trying to reach content of eye as if eye is 2 dimensional js array, which is definitely not true. math.matrix returns some object, the best way to investigate it's structure is console.log(eye) (in Chrome or other modern browser). Or/and read docs about it methods
Related
If I have a JavaScript object such as:
const group = {
Gp1: 1,
Gp12:1,
Gp11:2,
Gp10: 0,
Gp2: 1,
Gp5: 4,
Gp3: 4,
Gp4: 4,
Gp6: 4,
};
I want to make sortable ascending by key of the array
with this code
const sortable = Object.fromEntries(
Object.entries(group).sort(([,a],[,b]) => a-b)
);
console.log(sortable);
I end up with
{
Gp1: 1,
Gp10: 0,
Gp11: 2,
Gp12: 1,
Gp2: 1,
Gp3: 4,
Gp4: 4,
Gp5: 4,
Gp6: 4
}
Is there a way to sort the properties based on key array? So that I end up with
{
Gp1: 1,
Gp2: 1,
Gp3: 4,
Gp4: 4,
Gp5: 4,
Gp6: 4,
Gp10: 0,
Gp11: 2,
Gp12: 1,
}
You can use string#localeCompare to sort based on numeric value by passing {numeric: true}.
const o = { Gp1: 1, Gp10: 0, Gp11: 2, Gp12: 1, Gp2: 1, Gp3: 4, Gp4: 4, Gp5: 4, Gp6: 4 },
result = Object.fromEntries(Object.entries(o).sort((a,b) => a[0].localeCompare(b[0], undefined, {numeric: true})));
console.log(result);
Are you finding something like this?
const sortable = Object.fromEntries(
Object.entries(group).sort(([a,],[b,]) => parseInt(a.replace('Gp', '')) - parseInt(b.replace('Gp', '')))
);
how can i flatten an array without using flat(). by 1 level?
so far i have this
function flatten(array) {
let flattened = [];
for (let i = 0; i < array.length; i++) {
const current = array[i];
for (let j = 0; i < current.length; j++) {
flattened.push(current[j])
}
}
return flattened
}
console.log(flatten([['foo', 'bar'], ['baz', 'qux']]));
// -> ["foo", "bar", "baz", "qux"]
flatten([[1], [2], 3, 4, [5]]);
// -> [1, 2, 3, 4, 5]
flatten([false, [true, [false]], [true]]);
// -> [false, true, [false], true]
flatten([]);
// -> []
and its crashing my memory
I hope this helps
var twoDimension = [[1], [2], 3, 4, [5]];
var plano = twoDimension.reduce((acc, el) => acc.concat(el), []);
console.log(plano);
You could use Array.reduce and the spread syntax:
function flatten(array) {
return array.reduce(
(accumulator, item) => {
// if `item` is an `array`,
// use the `spread syntax` to
// append items of the array into
// the `accumulator` array
if (Array.isArray(item)) {
return [...accumulator, ...item];
}
// otherwise, return items in the
// accumulator plus the new item
return [...accumulator, item];
}
, []); // initial value of `accumulator`
}
console.log(flatten([['foo', 'bar'], ['baz', 'qux']]));
// -> ["foo", "bar", "baz", "qux"]
console.log(flatten([[1], [2], 3, 4, [5]]));
// -> [1, 2, 3, 4, 5]
console.log(flatten([false, [true, [false]], [true]]));
// -> [false, true, [false], true]
console.log(flatten([]));
// -> []
References:
Array.reduce - MDN
Spread syntax - MDN
To flatten by a single level only, Array#concat() can be leveraged. It accepts any amount of arguments, so an array can be spread into the function call:
[].concat(...arr)
This avoids any explicit loops. JavaScript handles everything:
function flatten(arr) {
return [].concat(...arr);
}
console.log(flatten([['foo', 'bar'], ['baz', 'qux']]));
// -> ["foo", "bar", "baz", "qux"]
console.log(flatten([[1], [2], 3, 4, [5]]));
// -> [1, 2, 3, 4, 5]
console.log(flatten([false, [true, [false]], [true]]));
// -> [false, true, [false], true]
console.log(flatten([]));
// -> []
You can use the following method if your array have primitive data type and want to flat it completely:
arr.toString().split(',');
you can use the reducer of javascript as an alternative to flat().
const arr = [1, 2, [3, 4]];
arr.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4]
or you can use decomposition syntax
const flattened = arr => [].concat(...arr);
For more details, go to Mozilla MDN
You have an error here:
for (let j = 0; i < current.length; j++) {
// ^ wrong variable, should be j
And you need to check if the value is not an array, then just push the current value and continue the loop.
function flatten(array) {
let flattened = [];
for (let i = 0; i < array.length; i++) {
const current = array[i];
if (!Array.isArray(current)) {
flattened.push(current);
continue;
}
for (let j = 0; j < current.length; j++) {
flattened.push(current[j])
}
}
return flattened
}
console.log(flatten([['foo', 'bar'], ['baz', 'qux']]));
// -> ["foo", "bar", "baz", "qux"]
console.log(flatten([[1], [2], 3, 4, [5]]));
// -> [1, 2, 3, 4, 5]
console.log(flatten([false, [true, [false]], [true]]));
// -> [false, true, [false], true]
console.log(flatten([]));
// -> []
You had a typo where in your innermost loop you set i to 0 instead of j. The only other thing you needed to do was check to see if each element in the outer array was scalar (not an array) and push it to the returned array if so.
function flatten(arr) {
let flat = []
for (let i=0; i < arr.length; i++) {
const cur = arr[i]
if(!Array.isArray(cur)){
flat.push(cur)
}else{
for (let j=0; j < cur.length; j++) {
flat.push(cur[j])
}
}
}
return flat
}
console.log(flatten([['foo','bar'],['baz','qux']]))
console.log(flatten([[1],[2],3,4,[5]]))
console.log(flatten([false,[true,[false]],[true]]))
console.log(flatten([]))
Well you can use spread operator with reduce.
function flatten(array) {
return array.reduce((a,v) => [...a, ...(Array.isArray(v) ? v : [v])], []);
}
console.log(flatten([['foo', 'bar'], 'baz', 'qux']))
Following could be used as a general implementation of Array.prototype.flat()
function flattenArray(arr, depth = 1) {
if (!Array.isArray(arr)) {
return [arr];
}
return depth > 0
? arr.reduce(
(acc, curr) =>
acc.concat(
Array.isArray(curr) ? flattenArray(curr, depth - 1) : curr
),
[]
)
: arr.slice();
}
const a = [1, 2, 3, 4];
const b = "a";
const c = [1, [2, 3], 4];
const d = [1, [2, [3, 4], 5], 6];
const e = [1, [2, [3, [4, [5], [6]], 7], 8], 9];
console.log(flattenArray(a, Infinity));
console.log(flattenArray(b, Infinity));
console.log(flattenArray(c, Infinity));
console.log(flattenArray(d, Infinity));
console.log(flattenArray(e, Infinity));
There is another interesting way to do it.
Stringify the array
remove all array start symbol ([) and array end symbol(])
Add array start symbol at the beginning and array end symbol at the end.
Now parse the resulting string
const arr2 = [0, 1, 2, [5, [10, [3, 4]]]]
const arr2 = [0, 1, 2, [5, [10, [3, 4]]]]
console.log( JSON.parse('['+ JSON.stringify(arr2).replace(/\[/g, ' ').replace(/\]/g, ' ') + ']'))
Suppose given flatten number list without using the flat function is:
let array = [2,3,[5,2,[6,[3, [4, 5, [5, 1, 3]]]],1,1],9];
//let array= [2,3,[5,2,[6,[3, [4, 5, [5, {"key":"value"}, 3]]]],1,1],9];
//achieve above commented nested array condition using second approach.
The best answer already given by #Mahipal that would be first approach i.e.
array.toString().split(',')
with number array conversion
array.toString().split(',').map(n => +n)
another approach would be using function recursion without toString()
function flatter(arr) {
if (!Array.isArray(arr) && (!isNaN(arr) || typeof arr === "object")) {
return arr;
}
return arr.reduce((a, b) => {
a.push(...[].concat(flatter(b)));
return a;
}, [])
}
flatter(array);
and output is:
[ 2, 3, 5, 2, 6, 3, 4, 5, 5, 1, 3, 1, 1, 9 ]
Hope this would help many ones.
//Using Recursion
const arr = [1, 2, 3, 4, [5, 6, [6, 7], 7, 8]]
let arr2 = [];
function flat(arr) {
arr.forEach(element => {
if (typeof (element) == 'object') {
flat(element);
} else {
arr2.push(element);
}
});
}
flat(arr);
console.log(arr2);
var multiDimensionArray = [["a"],["b","c"],["d"]]; //array of arrays
var flatArray = Array.prototype.concat.apply([], multiDimensionArray); //flatten array of arrays
console.log(flatArray);
This worked for me:
function myFlattern(arr) {
let check;
do{
check=false;
for(let i=0;i<arr.length;i++)
{
if(Array.isArray(arr[i]))
{
check=true;
let x=arr[i];
arr.splice(i,1,...x);
}
}
}while(check)
return arr;
}
A possible alternative would be without using flat():
var arr = [['object1', 'object2'],['object1'],['object1','object2','object3']];
var flattened = [].concat.apply([],arr);
You can use this to forget about the depth of nesting:
let multiArr = [1, [1, 2, [3, 4]], [2, 4, [45, 98]]];
while (multiArr.find((elem) => Array.isArray(elem))) {
multiArr = [].concat.apply([], multiArr);
}
console.log(multiArr);
I am trying to add 4 nested arrays with 9 numeric values together in javascript and having issues. They are currently stored like: x = [[Array1(9)], [Array2(9)], [Array3(9)], [Array4(9)]]
I would like "zip" them together and add in the process so adding Array1[0] with Array2-4[0] to all nine values. So each of the four arrays summing up their matching ids.
Arrays look like this:
[1, 2, 4, 3.6, 1.05, 4.65, 1.95, 0.5, 2.5]
[0.432, 0.602, 1.29, 1.146, 0.558, 1.43, 0.686, 0.178, 1.024]
translating to:
[1.432, 2.602, ect...]
I can't seem to get a x.map() function to put them together the way I want. Any help would be appreciated.
Thanks,
You may traverse your outer array with Array.prototype.reduce() and loop through nested arrays one by one with Array.prototype.forEach() adding up nested array items to corresponding item of resulting array:
const src = [[1, 2, 4, 3.6, 1.05, 4.65, 1.95, 0.5, 2.5], [0.432, 0.602, 1.29, 1.146, 0.558, 1.43, 0.686, 0.178, 1.024]]
result = src.reduce((r,a) => (a.forEach((n,j) => r[j] = (r[j]||0)+n), r),[])
console.log(result)
.as-console-wrapper{min-height:100%;}
Using for each for outer and inner array
let arr1 =[ [1, 2, 4, 3.6, 1.05, 4.65, 1.95, 0.5, 2.5],
[0.432, 0.602, 1.29, 1.146, 0.558, 1.43, 0.686, 0.178, 1.024]]
let arr2=[];arr1.forEach( (arr)=> arr.forEach((innerArr,index)=>{
if(!arr2[index])arr2[index]=0;arr2[index]=arr2[index]+innerArr;
}));
console.log(arr2);
Use the first array for mapping, add to each value the corresponding values from the other 3 arrays. You can create a function for it for arrays of arbitrary length (see sumArrays in the snippet)
const arrays = getData();
let sums = arrays[0].map( (v, i) => v + arrays[1][i] + arrays[2][i] + arrays[3][i] );
let sums2 = sumArrays(arrays);
console.log(sums);
console.log(sums2);
function sumArrays(arrays) {
const checkLengthEquality = () => {
let cando = arrays.map(v => v.length);
return Math.max.apply(null, cando) === Math.min.apply(null, cando);
};
if (!checkLengthEquality()) {
throw Error("Arrays not of equal length");
}
const sum = (v, i) => {
let len = arrays.length;
while(len-- > 1) {
v += arrays[len][i];
}
return v;
};
return arrays[0].map( (v, i) => sum(v, i) );
}
function getData() {
return [
[1, 2, 4, 3.6, 1.05, 4.65, 1.95, 0.5, 2.5],
[0.432, 0.602, 1.29, 1.146, 0.558, 1.43, 0.686, 0.178, 1.024],
[3.1, 2, 4.3, 3.8, 1.5, 6.5, 95, 1.5, 2.5],
[12, 1, 1.3, 8, 5.3, 6.2, 5, 1, 2.3]
];
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
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))