How to find symmetrical difference using JavaScript? - javascript

I've been working on an algorithm that will find the symmetric difference of two arrays (i.e. only items that are in 1 of the 2 arrays, but not both). I've come up with the following so far:
function diffArray(arr1, arr2) {
let newArr1 = arr1.slice();
let newArr2 = arr2.slice();
if (newArr1.length > newArr2.length) {
return newArr1.filter((item) => !(item in newArr2));
} else {
return newArr2.filter((item) => !(item in newArr1));
}
};
const result = diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
console.log(result);
But when testing with diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]) the output is [4, 5] instead of just [4]. The issue seems to happen with whatever the last value of either array is, but I can't seem to figure out what about my code is causing the issue. Thanks in advance for any help.

The in operator checks if the value on the left matches a property name on the right.
In you want to check if one of the property values is in an array, use the includes method.
function diffArray(arr1, arr2) {
let newArr1 = arr1.slice();
let newArr2 = arr2.slice();
if (newArr1.length > newArr2.length) {
return newArr1.filter((item) => !newArr2.includes(item));
} else {
return newArr2.filter((item) => !newArr1.includes(item));
}
};
const result = diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
console.log(result);

Updating the answer for another condition
const result = diffArray([1, 2, 3, 4], [1, 2, 3, 5]);
function diffArray(arr1, arr2){
const diff1 = arr1.filter(item => !arr2.includes(item)); // if item not available, the value will be filtered
const diff2 = arr2.filter(item => !arr1.includes(item));// if item not available, the value will be filtered
return diff1.concat(diff2);
}
// const result = diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
const result = diffArray([1, 2, 3, 4], [1, 2, 3, 5]); // This condition will fail on the above code
console.log(result);

Related

Why is the splice function removing items from the beginning, not the end here? (Javascript)

I'm trying to write a function in Javascript that accepts a nested array and number as two arguments and returns a new nested array with the last couple items removed in each inside array as indicated by the number argument.
For example:
*/ removeColumns([[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]], 2);
=> [[1, 2],
[1, 2],
[1, 2],
[1, 2]]
*/
I am attaching the code have written so far. This gives me a return of [[3, 4], [3, 4]. I thought the splice function always removes array elements after the provided index but here it seems to be removing elements before the index. What am I doing wrong here?
const removeColumns = (originalGrid, numColumns) => {
for (let i = 0; i < originalGrid.length; i++) {
console.log(originalGrid[i].splice(originalGrid.length - numColumns, numColumns))
console.log(originalGrid[i])
}
return originalGrid
}
let originalGrid = [
[1, 2, 3, 4],
[1, 2, 3, 4]
];
console.log(removeColumns(originalGrid, 2))
I think that should fix your issue:
originalGrid[i].length - numColumns, numColumns)
In your example
let originalGrid = [
[1, 2, 3, 4],
[1, 2, 3, 4]
];
console.log(originalGrid.length) //2
console.log(originalGrid[0].length) //4
console.log(originalGrid[1].length) //4
So in the loop don't forget to add the index:
console.log(originalGrid[i].splice(originalGrid[i].length - numColumns, numColumns))
Write a filter function. Then, you can choose which column "section" to include.
const originalGrid = [
[1, 2, 3, 4],
[1, 2, 3, 4]
];
const filter = (data, from, to) => data.map(a => a.splice(from, to));
console.log(filter(originalGrid, 0, 2));

Assigning key's to array objects

I'm trying to solve this problem. Essentially, I have a array of keys, and an array of values within objects, and I want those values to have keys.
Below is my best attempt so far - usually use python so this is a bit confusing for me.
var numbers = [3, 4, 5,6]
var selection = [[1, 2, 3, 4], [6, 5, 4, 3], [2, 9, 4]]
var result = [];
for (arr in selection) {
numbers.forEach(function (k, i) {
result[k] = arr[i]
})
};
console.log(result);
The output I'm looking for is like this,
results = [{3:1,4:2,5:3,6:4}, {..},..]
Love some pointers to getting the right output.
Note. This is for google appscript! So can't use certain javascript functions (MAP I think doesn't work, unsure of reduce).
Cheers!
Use map on selection and Object.assign
var numbers = [3, 4, 5, 6];
var selection = [
[1, 2, 3, 4],
[6, 5, 4, 3],
[2, 9, 4]
];
var result = selection.map(arr =>
Object.assign({}, ...arr.map((x, i) => ({ [numbers[i]]: x })))
);
console.log(result);
Create a separate function which take keys and values as arguments and convert it into object using reduce(). Then apply map() on selections and make an object for each subarray using that function
var numbers = [3, 4, 5,6]
var selection = [[1, 2, 3, 4], [6, 5, 4, 3], [2, 9, 4]]
function makeObject(keys, values){
return keys.reduce((obj, key, i) => ({...obj, [key]: values[i]}),{});
}
const res = selection.map(x => makeObject(numbers, x));
console.log(res)
Create a new object from scratch for each number array:
const selection = [
[1, 2, 3, 4],
[6, 5, 4, 3],
[2, 9, 4],
];
function objMaker(numarr) {
const numbers = [3, 4, 5, 6];
numarr.forEach((num, i) => (this[numbers[i]] = num));
}
console.info(selection.map(numarr => new objMaker(numarr)));

How to remove one subset from an array in JS?

Let's say I have an array such as: [1, 1, 2, 2, 3, 3, 4, 5]
And I want to remove this array of elements [1, 2, 3, 4, 5]
So in the end I want to be left with [1, 2, 3]
I have tried using the method below but it removes all copies of the elements from the main array.
myArray = myArray.filter( function( el ) {
return !toRemove.includes( el );
} );
Here is a way to do it using filter, indexOf and splice.
const input = [1, 1, 2, 2, 3, 3, 4, 5];
function removeSubset(arr, subset) {
const exclude = [...subset];
return arr.filter(x => {
const idx = exclude.indexOf(x);
if (idx >= 0) {
exclude.splice(idx, 1);
return false;
}
return true;
});
}
console.log(removeSubset(input, [1, 2, 3, 4, 5]));
You could get a Map and count the values and filter by checking the count and decrement the count if found.
var array = [1, 1, 2, 2, 3, 3, 4, 5],
remove = [1, 2, 3, 4, 5],
map = remove.reduce((m, v) => m.set(v, (m.get(v) || 0) + 1), new Map),
result = array.filter(v => !map.get(v) || !map.set(v, map.get(v) - 1));
console.log(result);
One solution is looping on the array of elements to remove and for each one remove the first element found on the input array:
const input = [1, 1, 2, 2, 3, 3, 4, 5];
const removeItems = (input, items) =>
{
// Make a copy, to not mutate the input.
let clonedInput = input.slice();
// Search and remove items.
items.forEach(x =>
{
let i = clonedInput.findIndex(y => y === x);
if (i >= 0) clonedInput.splice(i, 1);
});
return clonedInput;
}
console.log(removeItems(input, [1,2,3,4,5]));
console.log(removeItems(input, [1,2]));
console.log(removeItems(input, [1,99,44,5]));
If you still want to use filter, you can use the items to remove as the this argument of the filter, something like this:
const input = [1, 1, 2, 2, 3, 3, 4, 5];
const removeItems = (input, items) =>
{
return input.filter(function(x)
{
let i = this.findIndex(y => y === x);
return i >= 0 ? (this.splice(i, 1), false) : true;
}, items.slice());
}
console.log(removeItems(input, [1,2,3,4,5]));
console.log(removeItems(input, [1,2]));
console.log(removeItems(input, [1,99,44,5]));
You can use Filter and Shitf and Sort
let arr = [1, 1, 2, 2, 3, 3, 4, 5]
let remove = [1, 3, 2, 4, 5].sort((a,b)=>a-b)
let op = arr.sort((a,b)=>a-b).filter(e => ( remove.includes(e) ? (remove.shift(), false) : true ))
console.log(op)

Symmetric Difference javascript

I am trying to solve this freecodecamp algorithm question where I had to collect the difference of two or more arrays. I used map to get the difference of array but the problem is I only get two elements;
function sym(args) {
args = [].slice.call(arguments);
var newArr = args.map(function(el, index, arr){
console.log(arr.indexOf(arr[index]));
if(arr.indexOf(arr[index] === -1 )){
// console.log(arr[index]);
return args.push(arr[index]);
}
});
return newArr; // my newArr returns [3, 4] instead of [3,4,5]
}
console.log(sym([1, 2, 3], [5, 2, 1, 4]));
//sym([1, 2, 3], [5, 2, 1, 4]) should return [3, 4, 5]
//sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]) should return [1, 2, 4, 5, 6, 7, 8, 9]
I think we could do also this way since we want them to be ordered at the end.
For more detail about the original problem please consult this link: FreecodeCamp Link: Symmetric Difference
const sym = (...args) => {
// Merge all the different arrays and remove duplicate elements it means elements that are present both on two related arrays
let tab = args.reduce((a, b) => [
...a.filter(i => !b.includes(i)),
...b.filter(j => !a.includes(j))
], []);
// Then remove the rest of duplicated values and sort the obtained array
return Array.from(new Set(tab)).sort((a, b) => a - b);
}
console.log(sym([1, 2, 3, 3], [5, 2, 1, 4])); // [3, 4, 5]
console.log(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5])); // [1, 4, 5]
console.log(sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1])); // [1, 2, 4, 5, 6, 7, 8, 9]
The Set data structure is used here to remove duplicated values thanks to its characteristics.
Well your function is a little more complex than only selecting the unique values, cause you want to filter them out... and also accept multiple arrays. This should work.
var sym = (...arrays)=>{
//Concat Items
const allItems = arrays.reduce((a,c)=>a.concat(c), []);
// Identify repeated items
const repeatedItems = allItems.filter((v,i,a)=>a.indexOf(v) !== i);
// Filter repeated items out
const diff = allItems.filter(item=>repeatedItems.indexOf(item) < 0);
console.log(diff);
};
sym([1, 2, 3], [5, 2, 1, 4]); // [3,5,4]
I don't think your approach will work; you're supposed to create an array with elementos from both arrays, so a single .map won't do the job. Filtering through both arrays should work, although it will probably leave enough room for optimization.
my newArr returns [3, 4] instead of [3,4,5]
You are using map which will only return one value per iteration (which is why you are getting only 2 values) and in your case you are checking if the index is found or not (not the item)
You need to concatenate all the arrays and then remove those which are repeated
Concatenate
var newArr = args.reduce( ( a, c ) => a.concat( c ) , []);
Create a map by number of occurrences
var map = newArr.reduce( (a,c) => ( a[c] = (a[c] || 0) + 1, a ) , {});
Iterate and filter through those keys whose value is 1
var output = Object.keys( map ).filter( s => map[s] === 1 ).map( Number );
Demo
function sym(args)
{
args = [].slice.call(arguments);
var newArr = args.reduce( ( a, c ) => a.concat( c ) , []);
var map = newArr.reduce( (a,c) => ( a[c] = (a[c] || 0) + 1, a ) , {});
return Object.keys( map ).filter( s => map[s] === 1 ).map( Number );
}
console.log(sym([1, 2, 3], [5, 2, 1, 4]));
You could take an Object for counting the items and return only the items which have a count.
function sym(array) {
return array.reduce((a, b) => {
var count = {};
a.forEach(v => count[v] = (count[v] || 0) + 1);
b.forEach(v => count[v] = (count[v] || 0) - 1);
return Object.keys(count).map(Number).filter(k => count[k]);
});
}
console.log(sym([[3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]]));

Convert an Array to unique values only while maintaining the correct sequence

I have the following code:
function uniteUnique(arr) {
//Create a single Array of value
arr = arguments[0].concat(arguments[1], arguments[2]);
//Reduce the Array to unique values only
arr = arr.reduce((pre, curr) => {
//Some function to reduce values
});
return arr;
}
uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
The goal is to produce a single Array containing only unique values while maintaining the order.
Currently it returns:
[1, 3, 2, 5, 2, 1, 4, 2, 1]
I'm wanting to reduce this to:
[1, 3, 2, 5, 4]
You can use Set for that:
function uniteUnique(...args) {
return [...new Set([].concat(...args))];
}
var u = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(u);
It maintains insertion order, and by nature only contains unique values.
In ES5 you could do it by maintaining the used values as properties of a temporary object, while building the result array:
function uniteUnique(/* args */) {
return [].concat.apply([], arguments).reduce(function (acc, v) {
if (!acc[0][v]) acc[0][v] = acc[1].push(v); // assigns new length, i.e. > 0
return acc;
}, [ Object.create(null), [] ])[1];
}
var u = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(u);
You can use the Set object since it already keeps your values unique in one object:
const mySet = new Set([1, 3, 2, 5, 2, 1, 4, 2, 1]);
// returns: Set { 1, 3, 4, 5 };
const arrayUniques = [...mySet];
console.log(arrayUniques);
// returns: [1, 3, 4, 5];

Categories

Resources