Related
I am trying to remove duplicates from my 2D array but no success.
Here is what I am doing any idea where am making mistake?
function Remove_duplicates_from_2d_array(data_array) {
if (data_array.length > 0) {
let unique_index_counter = 0;
// loop on target array
for (var a = 0; a < data_array.length; a++) {
var unique_array = data_array[unique_index_counter];
if (a === unique_index_counter) {
continue;
}
console.log('comparing index: ' + a + ' ( ' + data_array[a] + ' ) -- index: ' + a + ' ( ' + data_array[a] + ' )');
if (data_array[a].sort().join(',') === unique_array.sort().join(',')) {
console.log('match it index ' + a + ' - ' + unique_index_counter);
// same arrays
data_array.splice(a, 1);
a = 0; // reset for loop as splice will rebuilt array
}
// a will be equal to data_array length incase there is no match found
if (a === data_array.length) {
unique_index_counter++;
}
if(unique_index_counter != data_array.length) {
a = 0; // reset for loop because we have not checked all items
}
}
return data_array;
} else {
return [];
}
}
var a1 = [1, 2, 3];
b1 = [4, 4, 5];
c1 = [3, 4, 5];
d1 = [4, 4, 5];
var data_array = [];
data_array.push(a1);
data_array.push(b1);
data_array.push(c1);
data_array.push(d1);
console.log('original array.');
console.log(data_array);
var r = Remove_duplicates_from_2d_array(data_array);
console.log('unique array.');
console.log(r); // [[1,2,3],[4,4,5],[3,4,5]]
You could take a Set for seen sorted string and filter the array by checking the occurences.
function removeDuplicates(array) {
var seen = new Set;
return array.filter(a => (s => !seen.has(s) && seen.add(s))(a.join()));
}
console.log(removeDuplicates([[1, 2, 3], [4, 4, 5], [3, 4, 5], [4, 4, 5]]));
// [[1, 2, 3], [4, 4, 5], [3, 4, 5]]
.as-console-wrapper { max-height: 100% !important; top: 0; }
With an object.
function removeDuplicates(array) {
var seen = {};
return array.filter(v => !seen[v] && (seen[v] = true));
}
console.log(removeDuplicates([[1, 2, 3], [4, 4, 5], [3, 4, 5], [4, 4, 5]]));
// [[1, 2, 3], [4, 4, 5], [3, 4, 5]]
.as-console-wrapper { max-height: 100% !important; top: 0; }
Sets are nice and all, but you may benefit from seeing a simple for-based approach too:
var orig = [[1, 2, 3],
[4, 4, 5],
[3, 4, 5],
[4, 4, 5]];
function undupe(arr){
let ret=[];
for(let candidate of arr){
let found=false;
for(let line of ret){
let linematch=true;
for(let i=0;i<line.length;i++){
if(candidate[i]!==line[i]){
linematch=false; // if one element does not match, the lines are different
break; // and the rest does not matter
}
}
if(linematch){
found=true; // if all elements matched, candidate is a duplicate
break; // remaining lines do not matter
}
}
if(!found){
ret.push(candidate); // candidate is unique, add it to the result
}
}
return ret;
}
console.log(undupe(orig));
candidate loop gets all items in the input array
line loop gets all items from ret array, so the items which are unique so far
i loop compares the actual numbers (one from candidate with the corresponding one from line)
these loop things are slow at large data sizes, so it is a good idea to terminate them as soon as possible. Like when the first elements do not match in a pair of lines, they are different for sure, there is no need to check the rest.
Of course it could be more structured, like
var orig = [[1, 2, 3],
[4, 4, 5],
[3, 4, 5],
[4, 4, 5]];
function linesmatch(line1,line2){
for(let i=0;i<line1.length;i++)
if(line1[i]!==line2[i])
return false;
return true;
}
function isdupe(arr,line){
for(let l of arr)
if(linesmatch(l,line))
return true;
return false;
}
function undupe(arr){
let ret=[];
for(let candidate of arr)
if(!isdupe(ret,candidate))
ret.push(candidate);
return ret;
}
console.log(undupe(orig));
This would be the more textbook-like variant with small, readable functions built on top of each other.
You may Array.prototype.reduce() source array doing Array.prototype.find()'ing duplicates along the way:
const src = [[1,2,3],[4,4,5],[3,4,5],[4,4,5]],
arrsAreEqual = (a1,a2) => a1.sort().join('|') === a2.sort().join('|'),
dedupe = src.reduce((r,a) =>
(!r.find(ra => arrsAreEqual(a,ra)) && r.push(a), r), [])
console.log(JSON.stringify(dedupe))
.as-console-wrapper{min-height:100%;}
here is my fixed simple version of duplicate remover based on for loop
function Remove_duplicates_from_2d_array(data_array) {
var unique_array = [];
if (data_array.length > 0) {
let unique_index_counter = 0;
// loop on target array
for (var a = 0; a < data_array.length; a++) {
// if have same indexs , skip
if (a === unique_index_counter) {
continue;
}
// compare both indexes
if (data_array[unique_index_counter].join(',') == data_array[a].join(',') ) {
data_array.splice(a, 1); // remove that a index
a = 0; // reset for loop as splice will rebuilt array
continue;
}
// a will be equal to data_array length incase there is no match found
else if ( (data_array.length != 0 && a == data_array.length - 1) ) {
unique_array.push(data_array[unique_index_counter]); // push unique index to unique array
data_array.splice(unique_index_counter, 1); // remove that a index
a = 0; // reset for loop as splice will rebuilt array
}
} // for end
// by now one unique element will be still left in source arrays
unique_array.push(data_array[0]); // push unique index to unique array
return unique_array;
} else {
return [];
}
}
var a1 = [1, 2, 3];
b1 = [4, 4, 5];
c1 = [3, 4, 5];
d1 = [4, 4, 5];
var data_array = [];
data_array.push(a1);
data_array.push(b1);
data_array.push(c1);
data_array.push(d1);
console.log('original array.');
console.log(data_array);
var r = Remove_duplicates_from_2d_array(data_array);
console.log('unique array.');
console.log(r); // [[1,2,3],[4,4,5],[3,4,5]]
I'm developing an annual permit calculator for work. I have to detect legal holidays. So I add all the legal holidays in my array. If date is equal to other array, it should return true. But the code says these two objects below are not equal. I couldn't figured out why.
var legalHolidays = [[1, 1, 2019], [23, 4, 2019], [1, 5, 2019], [19, 5, 2019], [3, 6, 2019]]
var otherArray = [1, 1, 2019]
if (legalHolidays[0] == otherArray) {console.log(true)}else{console.log(false)}
I expect the output of false to be true.
But the code says these two objects below are not equal. I couldn't figured out why.
They are not equal because they are instances of different arrays that happen to have same values. In order to bypass this, one thing you could do is to convert the arrays into json strings and compare them instead:
var legalHolidays = [[1, 1, 2019], [23, 4, 2019], [1, 5, 2019], [19, 5, 2019], [3, 6, 2019]];
var otherArray = [1, 1, 2019];
// the date arrays are converted to JSON strings. This enables the arrays
// to be compared by their respective string representations, avoiding
// the problem of having to deal with the different references
const match = legalHolidays.find(holiday => JSON.stringify(holiday) === JSON.stringify(otherArray));
console.log(`Is found: ${!!match}`);
NOTE:
Converting the arrays to Json strings is not the only way of comparing them. Essentially, you need to reduce the problem to comparing primitives (which are compared by value, rather than by reference as it is done in case of objects).
== is comparing the reference to the actual array instances, not the value of the array. You could create a function that converts to Date strings and compares them:
const otherArray = [1, 1, 2019];
var legalHolidays = [
[1, 1, 2019],
[23, 4, 2019],
[1, 5, 2019],
[19, 5, 2019],
[3, 6, 2019]
]
function isSameDate(holidayItem, comparisonItem) {
const [d1, m1, y1] = holidayItem;
const [d2, m2, y2] = comparisonItem;
if (new Date(y1, (m1 - 1), d1).toString() === new Date(y2, (m2 - 1), d2).toString()) {
return true;
}
return false;
}
legalHolidays.forEach(holiday => {
console.log(`Matches otherDate: ${isSameDate(holiday, otherArray)}`);
});
JavaScript compares arrays by reference when using equality operators, so if they are not the exact same object it will return false. You must either use JSON.stringify() to compare the string versions of the arrays:
if (JSON.stringify(legalHolidays[0]) == JSON.stringify(otherArray)) {
console.log(true);
} else {
console.log(false);
}
or manually compare the arrays at each index. In this example I use the every function:
const isLegal = legalHolidays[0].every((el, idx) => el === otherArray[idx]);
console.log(isLegal);
This is because == and === only compare by reference. What you would need to do is to check every element of otherArray against all the elements in the arrays contained in legalHolidays?
To do so, you can use [].some, which
... tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.
and a shallow equal function that compares two arrays elementwise. e.g. the one found in this answer
const legalHolidays = [
[1, 1, 2019],
[23, 4, 2019],
[1, 5, 2019],
[19, 5, 2019],
[3, 6, 2019]
]
const otherArray = [1, 1, 2019]
const shallowEqual = (a, b) => {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length != b.length) return false;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
// Please note that calling sort on an array will modify that array.
// you might want to clone your array first.
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
const contains = (arrA, arrB) => arrA.some((arr) => {
return shallowEqual(arr, arrB);
});
const contained = contains(legalHolidays, otherArray);
console.log(contained)
Please do not use the shallowEqual function provided in this answer, as it is not a complete implementation. It fails if you pass it something that is not array.
Try this:
var x = legalHolidays.filter(function (elem) {
return elem[0] == otherArray[0] && elem[1] == otherArray[1] && elem[2] == otherArray[2];
});
if(x.length > 0) {
// otherArray is present in legalHolidays
}
Or more efficient,
var x = legalHolidays.find(function (elem) {
return elem[0] == otherArray[0] && elem[1] == otherArray[1] && elem[2] == otherArray[2];
});
if(x) {
// otherArray is present in legalHolidays
}
Or if the dates are in sorted order, try binary search (time complexity O(logn)):
function findDate(date, arrayOfDates) {
var start = 0;
var end = arrayOfDates.length-1;
while(start < end) {
var mid = parseInt(start + (end - start)/2);
var elem = arrayOfDates[mid];
if(elem[0] == otherArray[0] && elem[1] == otherArray[1] && elem[2] == otherArray[2]) {
return mid;
}
if(elem[2] > date[2]) {
start = mid + 1;
}
else if(elem[2] < date[2]) {
end = mid - 1;
}
else if(elem[1] > date[1]) {
start = mid + 1;
}
else if(elem[1] < date[1]) {
end = mid - 1;
}
else if(elem[0] > date[0]) {
start = mid + 1;
}
else {
end = mid - 1;
}
}
}
console.log(findDate(otherArray, legalHolidays));
Little bit of more code, but efficient.
Furthermore, if all dates in legalHolidays are of the same year, you can skip the first 2 ifs in the while loop.
You have an array of tuples. (A tuple is an array of a well-defined size in which each index has a specific meaning.) Each of your tuples has has three elements. This is why you can destrucuture your tuple via const [foo, bar, baz] = myArray;
Then you can find your existing array rather straighforward by comparing each of its values:
const hasDay = (haystack, needle) => {
return haystack.some((holiday) => {
const [day1, month1, year1] = holiday;
const [day2, month2, year2] = needle;
return day1 === day2 && month1 === month2 && year2 === year1;
})
}
const legalHolidays = [[1, 1, 2019], [23, 4, 2019], [1, 5, 2019], [19, 5, 2019], [3, 6, 2019]]
console.log(
hasDay(legalHolidays, [1, 1, 2019]), // will be true
hasDay(legalHolidays, [1, 2, 2019]) // will be false
)
How to find the greatest number of times each element occurs in a nested array?
I am looking to find the greatest number of times each element occurs in any of the subarrays. I am NOT looking for the element that occurs the most times in the entire nested array.
Let's say my nested array is [[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]].
The number 2 appears two times in one of the subarrays. The number 3 appears two times in one of the subarrays. The number 5 appears one time in one of the subarrays.
The end result I am looking for is [2, 2, 3, 3, 5].
What is the best way to do this? Below is my approach, which is not very good.
function makeNewArray(arr) {
// add the # to the numbers that appear once and add the ## to the numbers that appear twice
for (var j = 0; j < arr.length; j++) {
for (var i = 0; i < arr[j].length; i++) {
if (arr[j][i] === arr[j][i+1]) {
arr[j][i] = arr[j][i] + '#';
arr[j][i+1] = arr[j][i+1] + '#';
} else {
arr[j][i] = arr[j][i] + '#';
}
}
}
// flatten the array
arr = arr.reduce(function(a, b) { return a.concat(b); });
// remove the duplicates from the array
arr = arr.filter(function(a, b) { return arr.indexOf(a) == b; });
// remove the ## and # from the array
for (var i = 0; i < arr.length; i++) {
arr[i] = parseInt(arr[i]);
}
return arr;
}
makeNewArray([[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]]);
Just based on your question and not the result you expect that I don't really get, here is a working solution that will find the highest number of occurrence.
var a = [
[2, 3, 5],
[3, 3, 5],
[2, 2, 3, 5]
];
var o = {};
var max = 0;
var highest = null;
for (var i = 0; i < a.length; i++) {
for (var j = 0; j < a[i].length; j++) {
if (!o.hasOwnProperty(a[i][j])) {
o[a[i][j]] = 1;
} else {
o[a[i][j]]++;
}
if (o[a[i][j]] > max) {
max = o[a[i][j]];
highest = a[i][j];
}
}
}
//this is the number with the highest occurence
console.log(highest);
This ES6 solution iterates the sub arrays, and creates a map of the values, then in it moves the highest values to a map of the entire array. Afterwards, we map the Map entries (no pan intended) to new arrays that are filled with the numbers according to their highest count, and flatten the result.
var data = [[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]];
var result = [].concat(... // flatten the end result
[... // convert the Map to entries array
data.reduce((r, s) => { // reduce the array into a map of counts
s.reduce((map, num) => map.set(num, (map.get(num) || 0) + 1), new Map) // get a Map of the current sub array counts
.forEach((v, k) => r.set(k, Math.max(r.get(k) || 0, v))); // update the global Map if the sub array count of a number is higher
return r;
}, new Map)]
.map((s) => Array.from({ length: s[1] }, () => s[0]))); // map the entries into new sub arrays
console.log(result);
Have an array arr =
[ [1,2] , [5,1], [23,24], [3,4], [22,23], [2,3]]
output needed = [[5,1,2,3,4],[22,23,24]]
tried using concat, reduce methods of array in javascript but could not achieve the desired result - any ideas?
I'm going to start by saying the code example and description is not very clear, but I'll take a shot...
It seems you have 3 requirements:
Flatten the original array.
Filter out duplicates.
Group values (somehow?) within the flattened array.
The first 2 tasks on the list can be accomplished using reduce, concat, filter and indexOf methods.
var multiArray = [ [1,2] , [5,1], [23,24], [3,4], [22,23], [2,3] ];
var flattened = multiArray.reduce(function (prev, curr) {
return prev.concat(curr);
}, []);
var unique = flattened.filter(function (element, index, array) {
return array.indexOf(element) === index;
});
However, I wont be able to give you an example for that last task until you make it clear how you're hoping to have these values grouped within the flattened array.
I'll update my original answer once clarified.
This is another solution yet i believe i can make it with less code and more functional. I don't enjoy using forEach() or flags like reduced much. This one recursively encodes the array items until there is nothing left to encode. Later I will give it another try to make it look more sleek.
var rawarr = [[1,2], [5,1], [23,24], [3,4], [22,23], [2,3]],
encodarr = (arr) => {
var reduced = false;
arr.forEach((c,i,a) => {
var sa = a.slice(i+1),
j = sa.findIndex(f => c[0] == f[f.length-1] || c[c.length-1] == f[0]);
arr[i] = !!~j ? c[0] == sa[j][sa[j].length-1] ? (reduced = true, a.splice(i+j+1,1)[0].concat(c.slice(1)))
: (reduced = true, c.slice(0,-1).concat(a.splice(i+j+1,1)[0]))
: c;
return arr;
});
return reduced ? encodarr(arr) : arr;
};
document.write("<pre>" + JSON.stringify(encodarr(rawarr)) + "</pre>");
Basically this problem can be solved with more loops and only one array (A) or with an object as reference and only one loop (B).
A
An in situ proposal with no object, but with more loops. It checks every array with the path information with each other. If a match is found, the two arrays are
var data = [[1, 2], [5, 1], [23, 24], [3, 4], [22, 23], [2, 3], [4, 5]],
result = function (data) {
function first(a) { return a[0]; }
function last(a) { return a[a.length - 1]; }
var i = 0, j, t;
outer: while (i < data.length - 1) {
j = i + 1;
while (j < data.length) {
t = data.splice(j, 1)[0];
if (first(data[i]) === last(t)) {
t.pop();
data[i] = t.concat(data[i]);
i = 0;
continue outer;
}
if (last(data[i]) === first(t)) {
t.shift();
data[i] = data[i].concat(t);
i = 0;
continue outer;
}
data.push(t);
j++;
}
i++
}
return data;
}(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
B
This is a single loop proposal which takes all parts and appends them if possible. Later all start arrays are returned as result.
var data = [[1, 2], [5, 1], [23, 24], [3, 4], [22, 23], [2, 3], [4, 5]],
result = function (data) {
var o = { start: {}, end: {} };
data.forEach(function (a) {
var temp;
if (o.end[a[0]]) {
if (o.start[a[1]] && a[0] !== o.start[a[1]][o.start[a[1]].length - 1] && a[1] !== o.end[a[0]][0]) {
temp = o.end[a[0]].concat(o.start[a[1]]);
o.start[temp[0]] = temp;
o.end[temp[temp.length - 1]] = temp;
delete o.start[a[1]];
} else {
o.end[a[1]] = o.end[a[0]];
o.end[a[1]].push(a[1]);
}
delete o.end[a[0]];
return;
}
if (o.start[a[1]]) {
o.start[a[0]] = o.start[a[1]];
o.start[a[0]].unshift(a[0]);
delete o.start[a[1]];
return;
}
temp = a.slice();
o.start[a[0]] = temp;
o.end[a[1]] = temp;
});
return Object.keys(o.start).map(function (k) { return o.start[k]; });
}(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
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]);