How to assert nested array with other expected array - javascript

How to easily assert nested array elements with another array values? I tried the following but it's not working as expected.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(element => console.log(expectedCells.includes(element)));
//Expected: true, true, false, true but it returns false, false, false, false

You can use Array#some to check if any of the array elements are in the other array.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(element => console.log(element.some(x => expectedCells.includes(x))));

expectedCells.includes(element) checks, if a nested array of actual is in the expectedCells array, which only contains strings, so the output is always false
What you need to do is to also iterate over the elements of the nested array.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(elements => {
for (let i=0; i<elements.length; i++) {
if (expectedCells.includes(elements[i])) {
return console.log(true)
}
return console.log(false)
}
});

There's a couple of things going on here.
First, you aren't comparing the elements inside of actual's subarrays. You're comparing the subarrays themselves.
actual.forEach(...) is executing something like this:
console.log(expectedCells.includes(['a']))
console.log(expectedCells.includes(['b', 'e']))
console.log(expectedCells.includes(['e']))
console.log(expectedCells.includes(['c']))
None of the array objects will match the letters a, b, c, or d from the expectedCells array.
You'll have to create a double loop:
actual.forEach(
elementArray =>
console.log(
elementArray.some(element => expected.includes(element))
)
)

Related

Merge and sort multiple arrays using their ordering logic

I have an array of arrays and they are sorted based on their given position.
For example:
const test_arrays = [
['H', 'A'],
['X', 'K', 'Z', 'H', 'B', 'A'],
['M', 'H', 'B'],
['X', 'M', 'D','H', 'T', 'B'],
['X', 'D', 'K', 'Z']
]
My aim is to merge them and remove duplicates but keeping the ordering. For instance, I am expecting to get a result of ['X', 'M', 'D', 'K', 'Z', 'H', 'T', 'B', 'A']
What I have tried so far:
Merge arrays and keep ordering
Similar to the post from the above, it is likely that there will be conflicts but here are the following rules:
If the final position of the item cannot be determined because of insufficient information, the first one should have an index before the second one.
E.g.
const arrays = [
['X', 'H'],
['X', 'C']
]
The result should be ['X','H','C']
Every items should appear in the final output
If an item is appearing in multiple arrays at different positions, the first appearance is the right one (skip others).
The problems of the solution from Merge arrays and keep ordering are:
The whole result array is misplaced when the first array does not start with 'X'.
The item is misplaced when the first item : When the array does not start with 'X', the new item 'M' will be positioned in the end of the result string instead of between 'X' and 'D'.
This is my result array using these codes from the link above:
var test_array_sorted=[];
test_arrays.forEach(array => {
array.forEach((item, idx) => {
// check if the item has already been added, if not, try to add
if(!~test_array_sorted.indexOf(item)) {
// if item is not first item, find position of his left sibling in result array
if(idx) {
const result_idx = test_array_sorted.indexOf(array[idx - 1]);
// add item after left sibling position
test_array_sorted.splice(result_idx + 1, 0, item);
return;
}
test_array_sorted.push(item);
}
});
});
My current result = ['H','T','B','A','X','K','Z','M','D']
Most noticeably, 'H' is the first in the item even though it should be after 'X','M','D','K','Z'. 'D' is now positioned in the end even though it should be before 'K','Z','H','B','A'.
Any help is welcomed and thank you so much in advance!
You could take an array of unique values of index zero of all arrays and filter it by looking to the indices on other arrays and remove if they have an index greater than one (currently implemented in a negative fashion). Then filter the arrays and remove empty arrays.
const
topSort = (arrays) => {
const result = [];
while (arrays.length) {
const
temp = [...new Set(arrays.map(([v]) => v))]
.filter(v => arrays.every(a => a.indexOf(v) < 1));
if (!temp.length) throw new Error('Circular reference!');
temp.forEach(v => {
result.push(v);
arrays = arrays.filter(a => {
if (a[0] === v) a.shift();
return a.length;
});
});
}
return result;
},
arrays = [['H', 'A'], ['X', 'K', 'Z', 'H', 'B', 'A'], ['M', 'H', 'B'], ['X', 'M', 'D', 'H', 'T', 'B'], ['X', 'D', 'K', 'Z']],
circularReference = [['x', 'c'], ['c', 'x']];
console.log(...topSort(arrays)); // X M D K Z H T B A
console.log(...topSort(circularReference)); // Exception: Circular reference!
Use .sort() to sort array
test_array_sorted.sort()

Using reduce without return keyword by looking for unique values in an array

I like to use Array.prototype.reduce() for several different scenarios in my code, it's pretty straightforward and handy.
Please observe in the 2 different solutions below the reduce() function takes 2 different initial values, in the first case is new Set() and the second is [].
In the below example the code uses reduce() without return keyword - Set one:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.add(c), new Set());
console.log(Array.from(result));
The next example is using still reduce() but here with a return keyword - Array one:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => {
a.find(e => e === c) ? null : a.push(c);
return a;
}, []);
console.log(result);
Question:
So the .add() function for Set returns the Set object itself. The .push() function for Array returns the length of the used Array.
The Set case helps me to shorten the code using .reduce() without return keyword because the above mentioned reason. In my second example I would like to use the solution without return keyword but still with Array somehow.
Is there any workaround solution to get the same result but without using return keyword in the second example? I would like to shorten the code further if possible.
Any help is appreciated.
You could take either the accumulator, if found or concat the element to the accumulator.
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.find(e => e === c) ? a : a.concat(c), []);
console.log(result);
Just to mention, Set takes a complete array with the constructor.
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = Array.from(new Set(data));
console.log(result);
Array#concat can add a new item to an array and returns a new array, so can works similar to Set#add. However, it still needs the conditional operator since you want to either add an element or nothing - for the latter case that's concatenating an array with an empty array:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.concat(a.some(e => e === c) ? [] : c), []);
console.log(result);
Alternatively, you can use spread syntax to again combine two arrays:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => [...a, ...(a.some(e => e === c) ? [] : c)], []);
console.log(result);
Neither of the two is perfect, to be honest. The existence of the conditional operator makes this harder to read when all one line but it's still an option.

Is there a way to extract two-dimensional array from nested dictionary, with its length being unknown

I need to extract the two-dimensional array (output) from the multidimensional dictionary (input) with the unknown length, every letter is the pointer to the next letter (string IDs are represented by the capital letters). Hence I need to build a 2-dimensional array full of 1-dimensional IDs, which I can use to get the other data, keeping this "every current ID is the pointer to the next ID" type of a data chain.
Input: {A:[B,C], B:[D, E], C:[F], D:[], F:[R], E:[]}
Output: [[A,B,D], [A,B,E], [A,C,F,R]]
Schematics:
A->B->D->END;
A->B->E->END;
A->C->F->R->END;
e.g.
"Room" -> "Shelf" -> "Book" -> "Paper"; "Lot" -> "Car" -> "Glove Compartment" -> "Candy"
let input = {A: ['B', 'C'], B: ['D', 'E'], C: ['F'], D: [], F: ['R'], E: []};
let start = 'A';
let x = k =>
input[k] && input[k].length ? input[k].map(v => x(v).map(r => [k, ...r])).flat() : [[k]];
let output = x(start);
console.log(output); // [['A', 'B', 'D'], ['A', 'B', 'E'], ['A', 'C', 'F', 'R']];

Quickest/most elegant way to verify a 2D array has same length per row?

Let's say I have a 2D array:
const matrixRegular = [
['a', 'b', 'c'],
['e', 'f', 'g'],
];
Let's say I want to verify that every row in this matrix has the same length, so the example above is a valid matrix, but the example below is not:
const matrixIrregular = [
['a', 'b', 'c'],
['e', 'f']
];
What's a clean/elegant way of doing it? This is the one-liner I have:
const isRegularMatrix = matrix => new Set(data.map(row => row.length)).size === 1
Convert the matrix to an array of just row length, then use Set to ensure every element is a duplicate (same length), hence it comes out with size 1.
You can use every() and compare length of each array with the length of the first.
const isRegularMatrix = matrix => matrix.every(x => x.length === matrix[0].length)

How to merge two arrays in Javascript?

I have two arrays:
var one = ['A', 'B', 'C'];
var two = ['a', 'b', 'c'];
I want to merge them and get this:
var three = ['Aa', 'Bb', 'Cc'];
I looked for answers but I only found concat(), which gives
['A', 'B', 'C', 'a', 'b', 'c']
That is not what I want.
How can I merge the two arrays?
You can do a simple map function:
var three=one.map((item,index)=>item+two[index])
Just need to assume they all have same indices.

Categories