Symmetric Difference of unknown number of arrays - javascript

Okay so I know there are multiple answers to this question but all of them use different approaches and I'm confused af rn.
The objective is to create a function that takes two or more arrays and returns an array of the symmetric difference of the provided arrays. The individual helper function is working fine but the code throws an error when I try to run it whole.
Here is my attempt:
function sym(args) {
let totalArguments = [...args];
var helper = function (arr1, arr2) {
return arr1.filter(item => arr2.indexOf(item) === -1).concat(arr2.filter(item => arr1.indexOf(item) === -1));
}
return totalArguments.reduce(helper);}
The input of sym([1, 2, 3],[2, 3, 4]) should be [1, 4]

Your code isn't working because you're only using the first argument:
function sym(args) {
let totalArguments = [...args];
This takes the first argument, args, and makes a shallow copy of the array - which doesn't accomplish anything because you aren't mutating anywhere anyway. If you wanted to accept a variable number of arguments, use argument rest syntax, to collect all arguments in an array:
function sym(...totalArguments) {
function sym(...totalArguments) {
var helper = function(arr1, arr2) {
return arr1.filter(item => arr2.indexOf(item) === -1).concat(arr2.filter(item => arr1.indexOf(item) === -1));
}
return totalArguments.reduce(helper);
}
console.log(sym([1, 2, 3], [2, 3, 4])) // [1, 4]
console.log(sym([1, 2, 3], [2, 3, 4], [0, 1])) // [0, 4], since 1 was duplicated
(There's no need to provide an initial value to the reducer)
Another option whose logic will probably be clearer to read and understand would be to iterate over all arrays and reduce into an object that keeps track of the number of times each number has occurred. Then, take the entries of the object, and return an array of the keys whose values are 1:
function sym(...args) {
const counts = args.reduce((a, arr) => {
arr.forEach((num) => {
a[num] = (a[num] || 0) + 1;
});
return a;
}, {});
return Object.entries(counts)
.filter(([, count]) => count === 1)
.map(([key]) => key);
}
console.log(sym([1, 2, 3], [2, 3, 4])) // [1, 4]
console.log(sym([1, 2, 3], [2, 3, 4], [0, 1])) // [0, 4], since 1 was duplicated

Related

Javascript: How to find difference for two number arrays

I need to create a new array made up of unique elements from two separate arrays.
I have converted both arrays into a single array and then converted this into an object to check the frequency of the elements. If the value of an object property is 1 (making it a unique property), I want to return it to an array (minus the value). Is there a straightforward way to achieve this?
Edits: Moved result outside for loop. Expected output should be [4]
function diffArray(arr1, arr2) {
var finalArr = [];
var countObj = {};
var newArr = [...arr1, ...arr2];
for (var i = 0; i < newArr.length; i++) {
if (!countObj[newArr[i]]) countObj[newArr[i]] = 0;
++countObj[newArr[i]];
}
for (var key in countObj) {
if (countObj[key] === 1) {
finalArr.push(key);
}
} return finalArr;
}
diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
If I understand correctly, you're wanting to find the difference between arr1 and arr2, and returns that difference (if any) as a new array of items (that are distinct in either array).
There are a number of ways this can be achieved. One approach is as follows:
function diffArray(arr1, arr2) {
const result = [];
const combination = [...arr1, ...arr2];
/* Obtain set of unique values from each array */
const set1 = new Set(arr1);
const set2 = new Set(arr2);
for(const item of combination) {
/* Iterate combined array, adding values to result that aren't
present in both arrays (ie exist in one or the other, "difference") */
if(!(set1.has(item) && set2.has(item))) {
result.push(item);
}
}
return result;
}
console.log(diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]), " should be [4]");
console.log(diffArray([1, 2, 3, 5, 8], [1, 2, 3, 5]), " should be [8]");
console.log(diffArray([1, 2, 3, 5, 8], [1, 2, 3, 5, 9]), " should be [8, 9]");
console.log(diffArray([1, 2], [1, 2]), " should be []");

How to subtract one array from another, element-wise, in javascript

If i have an array A = [1, 4, 3, 2] and B = [0, 2, 1, 2] I want to return a new array (A - B) with values [1, 2, 2, 0]. What is the most efficient approach to do this in javascript?
const A = [1, 4, 3, 2]
const B = [0, 2, 1, 2]
console.log(A.filter(n => !B.includes(n)))
Use map method
The map method takes three parameters in it's callback function like below
currentValue, index, array
var a = [1, 4, 3, 2],
b = [0, 2, 1, 2]
var x = a.map(function(item, index) {
// In this case item correspond to currentValue of array a,
// using index to get value from array b
return item - b[index];
})
console.log(x);
For Simple and efficient ever.
Check here : JsPref - For Vs Map Vs forEach
var a = [1, 4, 3, 2],
b = [0, 2, 1, 2],
x = [];
for(var i = 0;i<=b.length-1;i++)
x.push(a[i] - b[i]);
console.log(x);
const A = [1, 4, 3, 2]
const B = [0, 2, 1, 2]
const C = A.map((valueA, indexInA) => valueA - B[indexInA])
console.log(C) // [1, 2, 2, 0]
Here the map is returning the substraction operation for each number of the first array.
Note: this will not work if the arrays have different lengths
If you want to override values in the first table you can simply use forEach method for arrays forEach. ForEach method takes the same parameter as map method (element, index, array). It's similar with the previous answer with map keyword but here we are not returning the value but assign value by own.
var a = [1, 4, 3, 2],
b = [0, 2, 1, 2]
a.forEach(function(item, index, arr) {
// item - current value in the loop
// index - index for this value in the array
// arr - reference to analyzed array
arr[index] = item - b[index];
})
//in this case we override values in first array
console.log(a);
One-liner using ES6 for the array's of equal size in length:
let subResult = a.map((v, i) => v - b[i]); // [1, 2, 2, 0]
v = value, i = index
function subtract(operand1 = [], operand2 = []) {
console.log('array1', operand1, 'array2', operand2);
const obj1 = {};
if (operand1.length === operand2.length) {
return operand1.map(($op, i) => {
return $op - operand2[i];
})
}
throw new Error('collections are of different lengths');
}
// Test by generating a random array
function getRandomArray(total){
const pool = []
for (let i = 0; i < total; i++) {
pool.push(Math.floor(Math.random() * total));
}
return pool;
}
console.log(subtract(getRandomArray(10), getRandomArray(10)))
Time Complexity is O(n)
You can also compare your answer with a big collection of arrays.

Flattening an array recursively (withoout looping) javascript

I'm practicing recursion and am trying to flatten an array without looping (recursion only). As a first step I did the iterative approach and it worked, but am stuck on the pure recursion version:
function flattenRecursive(arr) {
if (arr.length === 1) {
return arr;
}
return Array.isArray(arr) ? arr = arr.concat(flattenRecursive(arr)) : flattenRecursive(arr.slice(1))
}
console.log(flattenRecursive([
[2, 7],
[8, 3],
[1, 4], 7
])) //should return [2,7,8,3,1,4,7] but isn't - maximum call stack error
//working version (thanks #Dave!):
function flattenRecursive(arr) {
if (arr.length === 1) {
return arr;
}
return arr[0].concat(Array.isArray(arr) ? flattenRecursive(arr.slice(1)) : arr);
}
console.log(flattenRecursive([
[2, 7],
[8, 3],
[1, 4], 7
]))
//returns [ 2, 7, 8, 3, 1, 4, 7 ]
Here's a working version that's slightly less verbose.
//using reduce
function flattenRecursive(arr) {
return arr.reduce(function(result, a){
return result.concat(Array.isArray(a) ? flattenRecursive(a) : a);
}, []);
}
//without reduce
function flattenRecursive2(arr) {
if (arr.length === 0)
return arr;
var head = arr.shift();
if (Array.isArray(head))
return flattenRecursive2(head).concat(flattenRecursive2(arr));
else
return [head].concat(flattenRecursive2(arr));
}
var testArray = [1,[2, 3],[[4, 5, [6, 7]], [8, 9]], 10];
console.log(flattenRecursive(testArray));
console.log(flattenRecursive2(testArray));
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
Sorry, I do not have the reputation to comment. Here are my 2 cents.
1) be careful about your initial condition. Here, it seems that if your input is ̀arr = [[1,2]], your function returns [[1,2]], while you would like it to return [1,2].
2) in the core of the recursion, you must be sure than you recursively call your function with a smaller argument. Here, you should concat the first element of your array with the flattened rest of the array. The functionslice` may be handy for that.
3) It would be also be possible to use a reduce-like function.

Iterate an array as a pair (current, next) in JavaScript

In the question Iterate a list as pair (current, next) in Python, the OP is interested in iterating a Python list as a series of current, next pairs. I have the same problem, but I'd like to do it in JavaScript in the cleanest way possible, perhaps using lodash.
It is easy to do this with a simple for loop, but it doesn't feel very elegant.
for (var i = 0; i < arr.length - 1; i++) {
var currentElement = arr[i];
var nextElement = arr[i + 1];
}
Lodash almost can do this:
_.forEach(_.zip(arr, _.rest(arr)), function(tuple) {
var currentElement = tuple[0];
var nextElement = tuple[1];
})
The subtle problem with this that on the last iteration, nextElement will be undefined.
Of course the ideal solution would simply be a pairwise lodash function that only looped as far as necessary.
_.pairwise(arr, function(current, next) {
// do stuff
});
Are there any existing libraries that do this already? Or is there another nice way to do pairwise iteration in JavaScript that I haven't tried?
Clarification: If arr = [1, 2, 3, 4], then my pairwise function would iterate as follows: [1, 2], [2, 3], [3, 4], not [1, 2], [3, 4]. This is what the OP was asking about in the original question for Python.
Just make the "ugly" part into a function and then it looks nice:
arr = [1, 2, 3, 4];
function pairwise(arr, func){
for(var i=0; i < arr.length - 1; i++){
func(arr[i], arr[i + 1])
}
}
pairwise(arr, function(current, next){
console.log(current, next)
})
You can even slightly modify it to be able to make iterate all i, i+n pairs, not just the next one:
function pairwise(arr, func, skips){
skips = skips || 1;
for(var i=0; i < arr.length - skips; i++){
func(arr[i], arr[i + skips])
}
}
pairwise([1, 2, 3, 4, 5, 6, 7], function(current,next){
console.log(current, next) // displays (1, 3), (2, 4), (3, 5) , (4, 6), (5, 7)
}, 2)
In Ruby, this is called each_cons (each consecutive):
(1..5).each_cons(2).to_a # => [[1, 2], [2, 3], [3, 4], [4, 5]]
It was proposed for Lodash, but rejected; however, there's an each-cons module on npm:
const eachCons = require('each-cons')
eachCons([1, 2, 3, 4, 5], 2) // [[1, 2], [2, 3], [3, 4], [4, 5]]
There's also an aperture function in Ramda which does the same thing:
const R = require('ramda')
R.aperture(2, [1, 2, 3, 4, 5]) // [[1, 2], [2, 3], [3, 4], [4, 5]]
Another solution using iterables and generator functions:
function* pairwise(iterable) {
const iterator = iterable[Symbol.iterator]();
let a = iterator.next();
if (a.done) return;
let b = iterator.next();
while (!b.done) {
yield [a.value, b.value];
a = b;
b = iterator.next();
}
}
console.log("array (0):", ...pairwise([]));
console.log("array (1):", ...pairwise(["apple"]));
console.log("array (4):", ...pairwise(["apple", "orange", "kiwi", "banana"]));
console.log("set (4):", ...pairwise(new Set(["apple", "orange", "kiwi", "banana"])));
Advantages:
Works on all iterables, not only arrays (eg. Sets).
Does not create any intermediate or temporary array.
Lazy evaluated, works efficiently on very large iterables.
Typescript version (playground):
function* pairwise<T>(iterable: Iterable<T>): Generator<[T, T], void> {
const iterator = iterable[Symbol.iterator]();
let a = iterator.next();
if (a.done) return;
let b = iterator.next();
while (!b.done) {
yield [a.value, b.value];
a = b;
b = iterator.next();
}
}
This answer is inspired by an answer I saw to a similar question but in Haskell: https://stackoverflow.com/a/4506000/5932012
We can use helpers from Lodash to write the following:
const zipAdjacent = function<T> (ts: T[]): [T, T][] {
return zip(dropRight(ts, 1), tail(ts));
};
zipAdjacent([1,2,3,4]); // => [[1,2], [2,3], [3,4]]
(Unlike the Haskell equivalent, we need dropRight because Lodash's zip behaves differently to Haskell's`: it will use the length of the longest array instead of the shortest.)
The same in Ramda:
const zipAdjacent = function<T> (ts: T[]): [T, T][] {
return R.zip(ts, R.tail(ts));
};
zipAdjacent([1,2,3,4]); // => [[1,2], [2,3], [3,4]]
Although Ramda already has a function that covers this called aperture. This is slightly more generic because it allows you to define how many consecutive elements you want, instead of defaulting to 2:
R.aperture(2, [1,2,3,4]); // => [[1,2], [2,3], [3,4]]
R.aperture(3, [1,2,3,4]); // => [[1,2,3],[2,3,4]]
d3.js provides a built-in version of what is called in certain languages a sliding:
console.log(d3.pairs([1, 2, 3, 4])); // [[1, 2], [2, 3], [3, 4]]
<script src="http://d3js.org/d3.v5.min.js"></script>
# d3.pairs(array[, reducer]) <>
For each adjacent pair of elements in the specified array, in order, invokes the specified reducer function passing the element i and element i - 1. If a reducer is not specified, it defaults to a function which creates a two-element array for each pair.
Here's a generic functional solution without any dependencies:
const nWise = (n, array) => {
iterators = Array(n).fill()
.map(() => array[Symbol.iterator]());
iterators
.forEach((it, index) => Array(index).fill()
.forEach(() => it.next()));
return Array(array.length - n + 1).fill()
.map(() => (iterators
.map(it => it.next().value);
};
const pairWise = (array) => nWise(2, array);
I know doesn't look nice at all but by introducing some generic utility functions we can make it look a lot nicer:
const sizedArray = (n) => Array(n).fill();
I could use sizedArray combined with forEach for times implementation, but that'd be an inefficient implementation. IMHO it's ok to use imperative code for such a self-explanatory function:
const times = (n, cb) => {
while (0 < n--) {
cb();
}
}
If you're interested in more hardcore solutions, please check this answer.
Unfortunately Array.fill only accepts a single value, not a callback. So Array(n).fill(array[Symbol.iterator]()) would put the same value in every position. We can get around this the following way:
const fillWithCb = (n, cb) => sizedArray(n).map(cb);
The final implementation:
const nWise = (n, array) => {
iterators = fillWithCb(n, () => array[Symbol.iterator]());
iterators.forEach((it, index) => times(index, () => it.next()));
return fillWithCb(
array.length - n + 1,
() => (iterators.map(it => it.next().value),
);
};
By changing the parameter style to currying, the definition of pairwise would look a lot nicer:
const nWise = n => array => {
iterators = fillWithCb(n, () => array[Symbol.iterator]());
iterators.forEach((it, index) => times(index, () => it.next()));
return fillWithCb(
array.length - n + 1,
() => iterators.map(it => it.next().value),
);
};
const pairWise = nWise(2);
And if you run this you get:
> pairWise([1, 2, 3, 4, 5]);
// [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ]
We can wrap Array.reduce a little to do this, and keep everything clean.
Loop indices / loops / external libraries are not required.
If the result is required, just create an array to collect it.
function pairwiseEach(arr, callback) {
arr.reduce((prev, current) => {
callback(prev, current)
return current
})
}
function pairwise(arr, callback) {
const result = []
arr.reduce((prev, current) => {
result.push(callback(prev, current))
return current
})
return result
}
const arr = [1, 2, 3, 4]
pairwiseEach(arr, (a, b) => console.log(a, b))
const result = pairwise(arr, (a, b) => [a, b])
const output = document.createElement('pre')
output.textContent = JSON.stringify(result)
document.body.appendChild(output)
Here's a simple one-liner:
[1,2,3,4].reduce((acc, v, i, a) => { if (i < a.length - 1) { acc.push([a[i], a[i+1]]) } return acc; }, []).forEach(pair => console.log(pair[0], pair[1]))
Or formatted:
[1, 2, 3, 4].
reduce((acc, v, i, a) => {
if (i < a.length - 1) {
acc.push([a[i], a[i + 1]]);
}
return acc;
}, []).
forEach(pair => console.log(pair[0], pair[1]));
which logs:
1 2
2 3
3 4
Here's my approach, using Array.prototype.shift:
Array.prototype.pairwise = function (callback) {
const copy = [].concat(this);
let next, current;
while (copy.length) {
current = next ? next : copy.shift();
next = copy.shift();
callback(current, next);
}
};
This can be invoked as follows:
// output:
1 2
2 3
3 4
4 5
5 6
[1, 2, 3, 4, 5, 6].pairwise(function (current, next) {
console.log(current, next);
});
So to break it down:
while (this.length) {
Array.prototype.shift directly mutates the array, so when no elements are left, length will obviously resolve to 0. This is a "falsy" value in JavaScript, so the loop will break.
current = next ? next : this.shift();
If next has been set previously, use this as the value of current. This allows for one iteration per item so that all elements can be compared against their adjacent successor.
The rest is straightforward.
My two cents. Basic slicing, generator version.
function* generate_windows(array, window_size) {
const max_base_index = array.length - window_size;
for(let base_index = 0; base_index <= max_base_index; ++base_index) {
yield array.slice(base_index, base_index + window_size);
}
}
const windows = generate_windows([1, 2, 3, 4, 5, 6, 7, 8, 9], 3);
for(const window of windows) {
console.log(window);
}
Simply use forEach with all its parameters for this:
yourArray.forEach((current, idx, self) => {
if (let next = self[idx + 1]) {
//your code here
}
})
Hope it helps someone! (and likes)
arr = [1, 2, 3, 4];
output = [];
arr.forEach((val, index) => {
if (index < (arr.length - 1) && (index % 2) === 0) {
output.push([val, arr[index + 1]])
}
})
console.log(output);
A modifed zip:
const pairWise = a => a.slice(1).map((k,i) => [a[i], k]);
console.log(pairWise([1,2,3,4,5,6]));
Output:
[ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ], [ 5, 6 ] ]
A generic version would be:
const nWise = n => a => a.slice(n).map((_,i) => a.slice(i, n+i));
console.log(nWise(3)([1,2,3,4,5,6,7,8]));
You can do this with filter and map:
let a = [1, 2, 3, 4]
console.log(a.filter((_,i)=>i<a.length-1).map((el,i)=>[el,a[i+1]]))
You can omit the filter part if you are ok with the last element being [4, undefined]:
let a = [1, 2, 3, 4]
console.log(a.map((el,i)=>[el,a[i+1]]))
Lodash does have a method that allows you to do this: https://lodash.com/docs#chunk
_.chunk(array, 2).forEach(function(pair) {
var first = pair[0];
var next = pair[1];
console.log(first, next)
})

Trying to solve symmetric difference using Javascript

I am trying to figure out a solution for symmetric
difference using javascript that accomplishes the following
objectives:
accepts an unspecified number of arrays as arguments
preserves the original order of the numbers in the arrays
does not remove duplicates of numbers in single arrays
removes duplicates occurring across arrays
Thus, for example,
if the input is ([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]),
the solution would be, [1, 1, 6, 5, 4].
I am trying to solve this as challenge given by an online
coding community. The exact instructions of the challenge
state,
Create a function that takes two or more arrays and returns an array
of the symmetric difference of the provided arrays.
The mathematical term symmetric difference refers to the elements in
two sets that are in either the first or second set, but not in both.
Although my solution below finds the numbers that are
unique to each array, it eliminates all numbers occuring
more than once and does not keep the order of the numbers.
My question is very close to the one asked at finding symmetric difference/unique elements in multiple arrays in javascript. However, the solution
does not preserve the original order of the numbers and does not preserve duplicates of unique numbers occurring in single arrays.
function sym(args){
var arr = [];
var result = [];
var units;
var index = {};
for(var i in arguments){
units = arguments[i];
for(var j = 0; j < units.length; j++){
arr.push(units[j]);
}
}
arr.forEach(function(a){
if(!index[a]){
index[a] = 0;
}
index[a]++;
});
for(var l in index){
if(index[l] === 1){
result.push(+l);
}
}
return result;
}
symsym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]); // => Desired answer: [1, 1, 6. 5. 4]
As with all problems, it's best to start off writing an algorithm:
Concatenate versions of the arrays, where each array is filtered to contain those elements which no array other than the current one contains
Then just write that down in JS:
function sym() {
var arrays = [].slice.apply(arguments);
return [].concat.apply([], // concatenate
arrays.map( // versions of the arrays
function(array, i) { // where each array
return array.filter( // is filtered to contain
function(elt) { // those elements which
return !arrays.some( // no array
function(a, j) { //
return i !== j // other than the current one
&& a.indexOf(elt) >= 0 // contains
;
}
);
}
);
}
)
);
}
Non-commented version, written more succinctly using ES6:
function sym(...arrays) {
return [].concat(arrays .
map((array, i) => array .
filter(elt => !arrays .
some((a, j) => i !== j && a.indexOf(elt) >= 0))));
}
Here's a version that uses the Set object to make for faster lookup. Here's the basic logic:
It puts each array passed as an argument into a separate Set object (to faciliate fast lookup).
Then, it iterates each passed in array and compares it to the other Set objects (the ones not made from the array being iterated).
If the item is not found in any of the other Sets, then it is added to the result.
So, it starts with the first array [1, 1, 2, 6]. Since 1 is not found in either of the other arrays, each of the first two 1 values are added to the result. Then 2 is found in the second set so it is not added to the result. Then 6 is not found in either of the other two sets so it is added to the result. The same process repeats for the second array [2, 3, 5] where 2 and 3 are found in other Sets, but 5 is not so 5 is added to the result. And, for the last array, only 4 is not found in the other Sets. So, the final result is [1,1,6,5,4].
The Set objects are used for convenience and performance. One could use .indexOf() to look them up in each array or one could make your own Set-like lookup with a plain object if you didn't want to rely on the Set object. There's also a partial polyfill for the Set object that would work here in this answer.
function symDiff() {
var sets = [], result = [];
// make copy of arguments into an array
var args = Array.prototype.slice.call(arguments, 0);
// put each array into a set for easy lookup
args.forEach(function(arr) {
sets.push(new Set(arr));
});
// now see which elements in each array are unique
// e.g. not contained in the other sets
args.forEach(function(array, arrayIndex) {
// iterate each item in the array
array.forEach(function(item) {
var found = false;
// iterate each set (use a plain for loop so it's easier to break)
for (var setIndex = 0; setIndex < sets.length; setIndex++) {
// skip the set from our own array
if (setIndex !== arrayIndex) {
if (sets[setIndex].has(item)) {
// if the set has this item
found = true;
break;
}
}
}
if (!found) {
result.push(item);
}
});
});
return result;
}
var r = symDiff([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]);
log(r);
function log(x) {
var d = document.createElement("div");
d.textContent = JSON.stringify(x);
document.body.appendChild(d);
}
One key part of this code is how it compares a given item to the Sets from the other arrays. It just iterates through the list of Set objects, but it skips the Set object that has the same index in the array as the array being iterated. That skips the Set made from this array so it's only looking for items that exist in other arrays. That allows it to retain duplicates that occur in only one array.
Here's a version that uses the Set object if it's present, but inserts a teeny replacement if not (so this will work in more older browsers):
function symDiff() {
var sets = [], result = [], LocalSet;
if (typeof Set === "function") {
try {
// test to see if constructor supports iterable arg
var temp = new Set([1,2,3]);
if (temp.size === 3) {
LocalSet = Set;
}
} catch(e) {}
}
if (!LocalSet) {
// use teeny polyfill for Set
LocalSet = function(arr) {
this.has = function(item) {
return arr.indexOf(item) !== -1;
}
}
}
// make copy of arguments into an array
var args = Array.prototype.slice.call(arguments, 0);
// put each array into a set for easy lookup
args.forEach(function(arr) {
sets.push(new LocalSet(arr));
});
// now see which elements in each array are unique
// e.g. not contained in the other sets
args.forEach(function(array, arrayIndex) {
// iterate each item in the array
array.forEach(function(item) {
var found = false;
// iterate each set (use a plain for loop so it's easier to break)
for (var setIndex = 0; setIndex < sets.length; setIndex++) {
// skip the set from our own array
if (setIndex !== arrayIndex) {
if (sets[setIndex].has(item)) {
// if the set has this item
found = true;
break;
}
}
}
if (!found) {
result.push(item);
}
});
});
return result;
}
var r = symDiff([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]);
log(r);
function log(x) {
var d = document.createElement("div");
d.textContent = JSON.stringify(x);
document.body.appendChild(d);
}
I came across this question in my research of the same coding challenge on FCC. I was able to solve it using for and while loops, but had some trouble solving using the recommended Array.reduce(). After learning a ton about .reduce and other array methods, I thought I'd share my solutions as well.
This is the first way I solved it, without using .reduce.
function sym() {
var arrays = [].slice.call(arguments);
function diff(arr1, arr2) {
var arr = [];
arr1.forEach(function(v) {
if ( !~arr2.indexOf(v) && !~arr.indexOf(v) ) {
arr.push( v );
}
});
arr2.forEach(function(v) {
if ( !~arr1.indexOf(v) && !~arr.indexOf(v) ) {
arr.push( v );
}
});
return arr;
}
var result = diff(arrays.shift(), arrays.shift());
while (arrays.length > 0) {
result = diff(result, arrays.shift());
}
return result;
}
After learning and trying various method combinations, I came up with this that I think is pretty succinct and readable.
function sym() {
var arrays = [].slice.call(arguments);
function diff(arr1, arr2) {
return arr1.filter(function (v) {
return !~arr2.indexOf(v);
});
}
return arrays.reduce(function (accArr, curArr) {
return [].concat( diff(accArr, curArr), diff(curArr, accArr) )
.filter(function (v, i, self) { return self.indexOf(v) === i; });
});
}
That last .filter line I thought was pretty cool to dedup an array. I found it here, but modified it to use the 3rd callback parameter instead of the named array due to the method chaining.
This challenge was a lot of fun!
// Set difference, a.k.a. relative compliment
const diff = (a, b) => a.filter(v => !b.includes(v))
const symDiff = (first, ...rest) =>
rest.reduce(
(acc, x) => [
...diff(acc, x),
...diff(x, acc),
],
first,
)
/* - - - */
console.log(symDiff([1, 3], ['Saluton', 3])) // [1, 'Saluton']
console.log(symDiff([1, 3], [2, 3], [2, 8, 5])) // [1, 8, 5]
Just use _.xor or copy lodash code.
Another simple, yet readable solution:
/*
This filters arr1 and arr2 from elements which are in both arrays
and returns concatenated results from filtering.
*/
function symDiffArray(arr1, arr2) {
return arr1.filter(elem => !arr2.includes(elem))
.concat(arr2.filter(elem => !arr1.includes(elem)));
}
/*
Add and use this if you want to filter more than two arrays at a time.
*/
function symDiffArrays(...arrays) {
return arrays.reduce(symDiffArray, []);
}
console.log(symDiffArray([1, 3], ['Saluton', 3])); // [1, 'Saluton']
console.log(symDiffArrays([1, 3], [2, 3], [2, 8, 5])); // [1, 8, 5]
Used functions: Array.prototype.filter() | Array.prototype.reduce() | Array.prototype.includes()
function sym(arr1, arr2, ...rest) {
//creating a array which has unique numbers from both the arrays
const union = [...new Set([...arr1,...arr2])];
// finding the Symmetric Difference between those two arrays
const diff = union.filter((num)=> !(arr1.includes(num) && arr2.includes(num)))
//if there are more than 2 arrays
if(rest.length){
// recurrsively call till rest become 0
// i.e. diff of 1,2 will be the first parameter so every recurrsive call will reduce // the arrays till diff between all of them are calculated.
return sym(diff, rest[0], ...rest.slice(1))
}
return diff
}
Create a Map with a count of all unique values (across arrays). Then concat all arrays, and filter non unique values using the Map.
const symsym = (...args) => {
// create a Map from the unique value of each array
const m = args.reduce((r, a) => {
// get unique values of array, and add to Map
new Set(a).forEach((n) => r.set(n, (r.get(n) || 0) + 1));
return r;
}, new Map());
// combine all arrays
return [].concat(...args)
// remove all items that appear more than once in the map
.filter((n) => m.get(n) === 1);
};
console.log(symsym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4])); // => Desired answer: [1, 1, 6, 5, 4]
This is the JS code using higher order functions
function sym(args) {
var output;
output = [].slice.apply(arguments).reduce(function(previous, current) {
current.filter(function(value, index, self) { //for unique
return self.indexOf(value) === index;
}).map(function(element) { //pushing array
var loc = previous.indexOf(element);
a = [loc !== -1 ? previous.splice(loc, 1) : previous.push(element)];
});
return previous;
}, []);
document.write(output);
return output;
}
sym([1, 2, 3], [5, 2, 1, 4]);
And it would return the output as: [3,5,4]
Pure javascript solution.
function diff(arr1, arr2) {
var arr3= [];
for(var i = 0; i < arr1.length; i++ ){
var unique = true;
for(var j=0; j < arr2.length; j++){
if(arr1[i] == arr2[j]){
unique = false;
break;
}
}
if(unique){
arr3.push(arr1[i]);}
}
return arr3;
}
function symDiff(arr1, arr2){
return diff(arr1,arr2).concat(diff(arr2,arr1));
}
symDiff([1, "calf", 3, "piglet"], [7, "filly"])
//[1, "calf", 3, "piglet", 7, "filly"]
My short solution. At the end, I removed duplicates by filter().
function sym() {
var args = Array.prototype.slice.call(arguments);
var almost = args.reduce(function(a,b){
return b.filter(function(i) {return a.indexOf(i) < 0;})
.concat(a.filter(function(i){return b.indexOf(i)<0;}));
});
return almost.filter(function(el, pos){return almost.indexOf(el) == pos;});
}
sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]);
//Result: [4,5,1]
function sym(args) {
var initialArray = Array.prototype.slice.call(arguments);
var combinedTotalArray = initialArray.reduce(symDiff);
// Iterate each element in array, find values not present in other array and push values in combinedDualArray if value is not there already
// Repeat for the other array (change roles)
function symDiff(arrayOne, arrayTwo){
var combinedDualArray = [];
arrayOne.forEach(function(el, i){
if(!arrayTwo.includes(el) && !combinedDualArray.includes(el)){
combinedDualArray.push(el);
}
});
arrayTwo.forEach(function(el, i){
if(!arrayOne.includes(el) && !combinedDualArray.includes(el)){
combinedDualArray.push(el);
}
});
combinedDualArray.sort();
return combinedDualArray;
}
return combinedTotalArray;
}
console.log(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]));
This function removes duplicates because the original concept of symmetric difference operates over sets. In this example, the function operates on the sets this way: ((A △ B) △ C) △ D ...
function sym(...args) {
return args.reduce((old, cur) => {
let oldSet = [...new Set(old)]
let curSet = [...new Set(cur)]
return [
...oldSet.filter(i => !curSet.includes(i)),
...curSet.filter(i => !oldSet.includes(i))
]
})
}
// Running> sym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4])
console.log(sym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]))
// Return> [1, 6, 5, 2, 4]
This works for me:
function sym() {
var args = [].slice.call(arguments);
var getSym = function(arr1, arr2) {
return arr1.filter(function(each, idx) {
return arr2.indexOf(each) === -1 && arr1.indexOf(each, idx + 1) === -1;
}).concat(arr2.filter(function(each, idx) {
return arr1.indexOf(each) === -1 && arr2.indexOf(each, idx + 1) === -1;
}));
};
var result = getSym(args[0], args[1]);
var len = args.length - 1, i = 2;
while (--len) {
result = [].concat(getSym(result, args[i]));
i++;
}
return result;
}
console.info(sym([1, 1, 2, 5], [2, 2, 3, 5], [6, 8], [7, 8], [9]));
Alternative: Use the lookup inside a map instead of an array
function sym(...vs){
var has = {};
//flatten values
vs.reduce((a,b)=>a.concat(b)).
//if element does not exist add it (value==1)
//or mark it as multiply found value > 1
forEach(value=>{has[value] = (has[value]||0)+1});
return Object.keys(has).filter(x=>has[x]==1).map(x=>parseInt(x,10));
}
console.log(sym([1, 2, 3], [5, 2, 1, 4],[5,7], [5]));//[3,4,7])
Hey if anyone is interested this is my solution:
function sym (...args) {
let fileteredArgs = [];
let symDiff = [];
args.map(arrayEl =>
fileteredArgs.push(arrayEl.filter((el, key) =>
arrayEl.indexOf(el) === key
)
)
);
fileteredArgs.map(elArr => {
elArr.map(el => {
let index = symDiff.indexOf(el);
if (index === -1) {
symDiff.push(el);
} else {
symDiff.splice(index, 1);
}
});
});
return (symDiff);
}
console.log(sym([1, 2, 3, 3], [5, 2, 1, 4]));
Here is the solution
let a=[1, 1, 2, 6]
let b=[2, 3, 5];
let c= [2, 3, 4]
let result=[...a,...b].filter(item=>!(a.includes(item) && b.includes(item) ))
result=[...result,...c].filter(item=>!(b.includes(item) && c.includes(item) ))
console.log(result) //[1, 1, 6, 5, 4]
Concise solution using
Arrow functions
Array spread syntax
Array filter
Array reduce
Set
Rest parameters
Implicit return
const symPair = (a, b) =>
[...a.filter(item => !b.includes(item)),
...b.filter(item => !a.includes(item))]
const sym = (...args) => [...new Set(args.reduce(symPair))]
The function symPair works for two input arrays, and the function sym works for two or more arrays, using symPair as a reducer.
const symPair = (a, b) =>
[...a.filter(item => !b.includes(item)),
...b.filter(item => !a.includes(item))]
const sym = (...args) => [...new Set(args.reduce(symPair))]
console.log(sym([1, 2, 3], [2, 3, 4], [6]))
const removeDuplicates = (data) => Array.from(new Set(data));
const getSymmetric = (data) => (val) => data.indexOf(val) === data.lastIndexOf(val)
function sym(...args) {
let joined = [];
args.forEach((arr) => {
joined = joined.concat(removeDuplicates(arr));
joined = joined.filter(getSymmetric(joined))
});
return joined;
}
console.log(sym([1, 2, 3], [5, 2, 1, 4]));
Below code worked fine all the scenarios. Try the below code
function sym() {
var result = [];
for (var i = 0; i < arguments.length; i++) {
if (i == 0) {
var setA = arguments[i].filter((val) => !arguments[i + 1].includes(val));
var setB = arguments[i + 1].filter((val) => !arguments[i].includes(val));
result = [...setA, ...setB];
i = i + 1;
} else {
var setA = arguments[i].filter((val) => !result.includes(val));
var setB = result.filter((val) => !arguments[i].includes(val));
result = [...setA, ...setB];
}
}
return result.filter((c, index) => {
return result.indexOf(c) === index;
}).sort();
}
My code passed all test cases for the similar question on freecodecamp. Below is code for the same.
function sym(...args) {
const result = args.reduce((acc, curr, i) => {
if (curr.length > acc.length) {
const arr = curr.reduce((a, c, i) => {
if(a.includes(c)){
}
if (!acc.includes(c) && !a.includes(c)) {
a.push(c);
}
if (!curr.includes(acc[i]) && i < acc.length) {
a.push(acc[i])
}
return a;
}, []);
return [...arr];
} else {
const arr = acc.reduce((a, c, i) => {
if(a.includes(c)){
}
if (!curr.includes(c) && !a.includes(c)) {
a.push(c);
}
if (!acc.includes(curr[i]) && i < curr.length) {
a.push(curr[i])
}
return a;
}, []);
return [...arr]
}
});
let ans = new Set([...result])
return [...ans]
}
sym([1,2,3,3],[5, 2, 1, 4,5]);

Categories