Finding the symmetric difference in a loop - javascript

I need to write a function that can take an indefinite number of arrays containing integer numbers and it should return 1 array which is the accumulative symmetrical difference between those arrays. Only two arrays are compared at a time. So [1, 2, 3], [3, 4, 2], [1, 5, 3] would first result in [1, 4], (comparing the first two arrays), which is then compared to the third and the final result is [4, 5, 3]. I created a loop that does that for the first two arrays, but I don't know how to turn it into an actual loop that performs the same operation on each step. For some reason using arr[i] and arr[i + 1] throws an error. Here's my code so far.
function test(...arr) {
let accumulator;
for (let i = 0; i < arr.length; i++) {
let common = arr[0].filter(a => arr[1].includes(a))
let arr0 = arr[0].filter(a => !common.includes(a))
let arr1 = arr[1].filter(a => !common.includes(a))
let merged = [...arr0, ...arr1]
accumulator = merged
}
return accumulator
}
console.log(test([1, 2, 3], [3, 4, 2], [1, 5, 3]))
Here accumulator is [1, 4], so at this point the entire operation needs to be performed with the next array and the accumulator, which is where I'm stuck at.

You're iterating with i from 0 to arr.length - 1. arr[i + 1] is arr[arr.length] in the last iteration. It's out of bounds. You could change the loop condition to i < arr.length - 1.
Example:
function test(...arr) {
let accumulator;
for (let i = 0; i < arr.length - 1; i++) {
let common = arr[i].filter(a => arr[i + 1].includes(a))
let arr0 = arr[i].filter(a => !common.includes(a))
let arr1 = arr[i + 1].filter(a => !common.includes(a))
let merged = [...arr0, ...arr1]
accumulator = merged
}
return accumulator
}
console.log(test([1, 2, 3], [3, 4, 2], [1, 5, 3]))

Related

Best way to loop through an array and return the sub arrays that match the first 2 values

Let's say I have 4 arrays:
var arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2]
];
And I want to return the child/sub arrays that start with both 1 and 2, what type of loop would I need?
Currently, this is what I have:
var arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2]
];
var selected = [1, 2]; // These are the values that need to match
var result = [];
for (var i = 0; i < selected.length; i++) {
for (var j = 0; j < arrays.length; j++) {
if (arrays[i][j] === selected[i]) {
result.push(arrays[i]);
}
}
}
When there's more than 1 value in the selected array, it seems to return all the ones that match 2 on the second index, so the result would be:
[
[1, 2, 1],
[1, 2, 3],
[0, 2, 2]
]
The loop needs to ensure that on the second iteration it's making sure the first value is still true, as my intended result would be:
[
[1, 2, 1],
[1, 2, 3]
]
Please someone help me, I've had my head trying hundreds of different loop and checks variations for 2-3 days.
Thanks so much!!
Jake
Your current code pushes to the result array whenever any given index matches between arrays and selected. Instead you will need to reverse your loops and iterate over selected for every sub array and check if every element matches, if not break the inner loop and don't push.
const arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2],
];
const selected = [1, 2]; // These are the values that need to match
const result = [];
for (let i = 0; i < arrays.length; i++) {
let match = true;
for (let j = 0; j < selected.length; j++) {
if (arrays[i][j] !== selected[j]) {
match = false;
break;
}
}
if (match) {
result.push(arrays[i]);
}
}
console.log(result);
A more modern solution would be to use filter() with a nested every() call on selected.
const arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2],
];
var selected = [1, 2];
const result = arrays.filter(arr => selected.every((n, i) => n === arr[i]));
console.log(result);
Here is another approach where you turn both arrays to string and check it those inner arrays start with selected array.
var arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2]
];
var selected = [1, 2];
const result = arrays.filter(e => e.toString().startsWith(selected.toString()))
console.log(result)
Let's try to put your condition into words. That way, an implementation may come to mind more easily.
A short wording may be: "Take all arrays that match (rather: start with) a certain sub-array." In code, it may look like this:
const arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2]
];
const selection = [1, 2];
const result = filterArrays(arrays, selection);
console.log(result);
function filterArrays(arrays, selection) {
const selectedArrays = [];
for (let i = 0; i < arrays.length; ++i) {
const array = arrays[i];
const subarray = array.slice(0, selection.length); // Get starting sub-array
if (compareArrays(subarray, selection)) {
selectedArrays.push(array);
}
}
return selectedArrays;
}
/*Ignore; helper function*/
function compareArrays(array1, array2) {
if (array1.length !== array2.length) return false;
const length = array1.length;
for (let i = 0; i < length; ++i) {
if (array1[i] !== array2[i]) return false;
}
return true;
}
.as-console-wrapper {max-height:100%!important}
Another, more specific wording may be: "Take all arrays that match a selection at an index." Note that we only reworded the "match a sub-array" part. I believe this is what you tried.
Refer to pilchard's answer for an implementation. Note that their implementation assumes the arrays in arrays to be at least the same length as selected.
I see you used var instead of the preferred modern let/const declarators. Here's a short outline of their differences:
let/const declarators:
Block-scoped.
Narrower scope means less name-space pollution.
More similar to declarators in other well-known languages:
Variables of these declarators cannot be used before their declaration (see TDZ).
var declarator:
Function-scoped.
Hoisted and with no TDZ, resulting in this (perhaps confusing) behaviour:
Variables declared with var can be used even before their declaration.
Duplicate declarations are allowed since they are effectively the same.
Also, JavaScript has different kinds of for-loops:
for-loop: The for-loops you used are this kind. It is the most versatile kind.
for...of-loop: A loop to iterate over an iterable object (see iterators). For example, arrays are iterable, so you can get its values with a for...of-loop:
const values = [1, 2, 3];
let sum = 0;
for (const value of array) {
sum += value;
}
console.log(sum); // -> 6
for...in-loop: A loop to iterate over enumerable properties of an object. It is easily confused with a for...of-loop, but MDN's example demonstrates the differences understandably.
In my code example above, the for-loop in filterArrays() can be replaced with a for...of-loop to better convey my intention: To iterate over all arrays in arrays, disregarding their index:
for (let i = 0; i < arrays.length; ++i) {
const array = arrays[i];
// ...
}
// Same as
for (const array of arrays) {
// ...
}

Javascript Multiplying Within an Array

I have an array with multiple arrays inside. I would like to multiply the contents of each array.
Here is my code--currently not getting anything in console.
The desired output is 3 numbers-- the result of multiplication of the three arrays
var arr = [
[1, 2, 3],
[3, 4],
[7, 8, 9]
]
var mult = 1
for (i = 0; i < arr.length; i++) {
for (j = 0; j < arr.length; j++) {
mult *= arr[i][j]
}
console.log(mult)
}
You could map the result of the nested multiplying.
const
multiply = (a, b) => a * b,
array = [[1, 2, 3], [3, 4], [7, 8, 9]],
result = array.map(a => a.reduce(multiply, 1));
console.log(result);
You can try this, it is easier using map and reduce functions. arr.map(array => array.reduce(arrayMult, 1)) is the multiplication of each inner array, like [ 6, 12, 504 ], and the last reduce is to multiply these values.
var arr = [[1, 2, 3],[3, 4],[7, 8, 9]];
const arrayMult = (prev, curr) => prev * curr;
const total = arr.map(array => array.reduce(arrayMult, 1)).reduce(arrayMult, 1);
You should iterate over rows. So you need to change inner loop on this :
for (j = 0; j < arr[i].length; j++)
const array = [
[1, 2, 3],
[3, 4],
[7, 8, 9]
];
for (i = 0; i < array.length; i++) {
let result = 1;
for (j = 0; j < array[i].length; j++) {
result *= array[i][j]
}
console.log(result);
}
Nothing wrong with good old iterating; your existing approach only had one bug, on the inner loop you were still checking the length of the top-level array instead of the inner array; j should range from 0 to arr[i].length, not arr.length.
An alternative approach uses map and reduce; comments below explain how it works:
var arr = [
[1, 2, 3],
[3, 4],
[7, 8, 9]
]
console.log(
arr.map(a => { // <-- calls a function on each element of arr,
// leaving the results in an array
return a.reduce( // <-- reducer is like map, calling a function
// on each element of a, but collects its results
// in a "collector" variable passed to each step
(val, sum) => {return val * sum}, // <-- multiply this value with
// the ones so far
1 // <-- initial value for the collector
)
})
)
One approach was to iterate the array of arrays via map which for each array puts the product of all of its items where the latter gets achieved via a multiplying reduce task.
console.log([
[1, 2, 3],
[3, 4],
[7, 8, 9],
].map(arr =>
arr.reduce((x, y) => x * y)
));

Using reduce method to get the multiplication of the last elements of each subarray

This is the array: [[1, 2], [1, 3], [1, 4]]
This is how it looks like with a regular for loop:
let multiplication = 1;
for (let i = 0; i < l.length; i++) {
multiplication *= l[i][1];
}
How do I do the same thing with reduce instead?
Initiate value at the second parameter of reduce, and with the callback function, remember to return the accumulated value
Below solution could help you
const arr = [
[1, 2],
[1, 3],
[1, 4],
];
const res = arr.reduce((acc, el) => acc * el[1], 1);
console.log(res);

Why isn't my javascript iterating through all of the arguments?

I'm working on The Odin Project and am on the Fundamentals Part 4 "removeFromArray" assignment. I have to return an array having removed all of the elements that are in the list of arguments. It works with two arguments, but I can't get it to work with four. My code is below:
const removeFromArray = function(firstArray, ...toRemove) {
let modifiedArray = firstArray;
for (i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
removeFromArray([1, 2, 3, 4], 7, 2) // works, returns [1, 3, 4]
removeFromArray([1, 2, 3, 4], 3, 2) // also works, returns [1, 4]
removeFromArray([1, 2, 3, 4], 1, 2, 3, 4) //does not work, returns [3, 4]
Any help is much appreciated.
Splicing from the array shifts all the remaining elements down by one, so you end up skipping over the next element. I'd recommend using Array.prototype.filter instead.
I think the real issue here, is that you may not be aware that arrays, objects, and functions in JavaScript are passed by reference.
let modifiedArray = firstArray;
Meaning your firstArray and modifiedArray are both pointing to the same array in memory. The code above assigns the EXACT same array, technically the address in memory of the firstArray to the modifiedArray. Therefore, as you remove items from the modifiedArray, you are also removing them from the firstArray, and therefore changing the length of the firstArray.
You need to copy the array by value, not by reference.
Solution:
Therefore changing
let modifiedArray = firstArray;
to
let modifiedArray = [...firstArray];
or
let modifiedArray = firstArray.slice();
The first solution leverages destructuring of the first array, to create a copy of the array by value, not pointing to the same array in memory.
The second may be more familiar to you as a beginner, since this simply returns a copy of the array, without removing any elements.
See this thread if you have more questions about copying arrays by value: Copy array by value
const removeFromArray = function(firstArray, ...toRemove) {
let modifiedArray = [...firstArray];
for (i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
console.log(removeFromArray([1, 2, 3, 4], 7, 2)); // works, returns [1, 3, 4]
console.log(removeFromArray([1, 2, 3, 4], 3, 2)); // also works, returns [1, 4]
console.log(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)); //does not work, returns [3, 4]
1, 2, 3, 4
After you remove the first item, everything suffles down, so what was the second item becomes the first item.
You don't have that problem if you start at the end and work in reverse (sort the list of items to remove).
I've changed the following line:
let modifiedArray = firstArray;
to
let modifiedArray = [...firstArray];
The problem you were facing is you were iterating over an array assuming it is a copy of the firstArray. But it was just pointing to firstArray. Now in the loop you startet removing items from the array and after you removed 2 in the iteration you were already on 2 so there was nothing left to iterate over...
const removeFromArray = function(firstArray, ...toRemove) {
let modifiedArray = [...firstArray];
for (var i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
var x = removeFromArray([1, 2, 3, 4], 7, 2);
var y = removeFromArray([1, 2, 3, 4], 3, 2);
var z = removeFromArray([1, 2, 3, 4], 1, 2, 3, 4);
console.log('x', x);
console.log('y', y);
console.log('z', z);
You take the same index for the array and for the items to remove, which may not be the same.
You could iterate the item to remove and get the index and splice this item from firstArray.
This approach muates the given array and keeps the same object reference.
const removeFromArray = function(firstArray, ...toRemove) {
for (let i = 0; i < toRemove.length; i++) {
let index = firstArray.indexOf(toRemove[i]);
if (index === -1) continue;
firstArray.splice(index, 1);
}
return firstArray;
}
console.log(...removeFromArray([1, 2, 3, 4], 7, 2)); // [1, 3, 4]
console.log(...removeFromArray([1, 2, 3, 4], 3, 2)); // [1, 4]
console.log(...removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)); // []
const removeFromArray = (firstArray, ...toRemove) =>
firstArray.filter(e => !toRemove.includes(e));
console.log(removeFromArray([1, 2, 3, 4], 7, 2));

Is there an efficient algorithm in JavaScript to find the number of distinct arrays within a larger array set?

Given the following array
let array = [[1, 2], [1, 2], [3, 4], [5, 6], [2, 1]]
I want to return the number of distinct arrays in this set. So the example above should return 3. How do I achieve this? I tried the code below, but it does not give the right answer
let distinct = 0
for (let i = 0; i < array.length; i++) {
for (let j = i + 1; j < array.length - i; j++) {
let difference = ingredients[i].filter(x => !array[j].includes(x))
if (difference.length > 0) {
distinct += 1;
}
}
}
return distinct;
If the order inside a sub item matters
Use Array.map() to convert each sub-array into a string (I've used String() as suggested by #trincot), create a Set from the array to remove duplicates, and get the size of the Set:
const array = [[1, 2], [1, 2], [3, 4], [5, 6]]
const distinct = new Set(array.map(String))
console.log(distinct.size)
If the order doesn't matter
Sort each sub item, and then convert to string:
const array = [[2, 1], [1, 2], [3, 4], [5, 6]]
const distinct = new Set(array.map(o => String(o.sort())))
console.log(distinct.size)
I didn't check your algorithm at all, just corrected your syntax. Is this what you meant to write?:
let array = [
[1, 2],[1, 2],[3, 4],[5, 6]
];
console.log(countDistinctArrays(array));
function countDistinctArrays(parentArray) {
let distinct = 0;
for (let i = 0; i < parentArray.length; i++) {
for (let j = i + 1; j < parentArray.length - i; j++) {
let difference = parentArray[i].filter(x => !parentArray[j].includes(x))
if (difference.length > 0) {
distinct += 1;
}
}
}
return distinct;
}
Here is how you could have tried. Try converting the inner arrays to a string, then filter the dupes and parse the string again
var pp = [[1, 2], [1, 2] ,[3, 4] ,[5, 6]];
var distict = pp.map(ar=>JSON.stringify(ar))
.filter((itm, idx, arr) => arr.indexOf(itm) === idx)
.map(str=>JSON.parse(str));
console.log(distict.length);

Categories

Resources