remove common elements between multiple arrays - javascript

I have 3 arrays (or more/less, it's not mandatory to be 3, I just gave an example) and I want to remove all the common elements between them. For example, between the first 2, the common elements are x and z, between the second and the third array the common element would be t. Between the first and the thirs the common element is k. Basically I want to remove any elements that appear more than 1 times in multiple arrays.
!! the first array can have common elements with the third one !!
Here is what I tried so far, but it's not working correctly.
let y = [{
id: 'a',
elems: ['x', 'y', 'z', 'k']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'k', 'o', 't']
},
]
// x, z, t
for (let i = 0; i < y.length - 1; i++) {
let current = y[i].elems
let current2 = y[i + 1].elems
if (current[i] == current2[i]) {
const index = current.indexOf(current[i]);
if (index > -1) {
current.splice(index, 1);
current2.splice(index, 1);
}
}
}
console.log(y)
The desired result would be
[
{
"id": "a",
"elems": [
"y"
]
},
{
"id": "b",
"elems": [
"u",
"i"
]
},
{
"id": "c",
"elems": [
"m",
"n",
"o"
]
}
]
Which would be a correct and optimal solution for this? I also tried to concatenate the 3 arrays and remove the duplicates, but then I don't know how to recreate the 3 arrays back.. Thanks!

I would first loop over all the elems and count up how many times that have been seen. After that I would loop again and filter out anything that was seen more than once.
const myData = [{
id: 'a',
elems: ['x', 'y', 'z']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'o', 't']
},
]
// count up every elem so we know which ones are duplicated
const allElems = myData.reduce((acc, item) => {
item.elems.forEach( key => {
acc[key] = acc[key] || 0;
acc[key]++;
});
return acc;
}, {})
// loop over all the elems and select only the elems that we have seen once
myData.forEach(item => {
item.elems = item.elems.filter(key => allElems[key] === 1);
})
console.log(myData)

let x = ['a', 'b']
let y = [{
id: 'a',
elems: ['x', 'y', 'z', 'k']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'k', 'o', 't']
},
]
// x, z, t
for (let i = 0; i < y.length - 1; i++) {
for (let j = 1; j < y.length; j++) {
let current = y[i].elems
let current2 = y[j].elems
current2.forEach((item,index)=>{
if(current.includes(item)){
current.splice(current.indexOf(item),1)
current2.splice(index,1)
}
})
}
}
console.log(y)
.as-console-wrapper { max-height: 100% !important; top: 0; }

const y = [
{ id: 'a', elems: ['x', 'y', 'z'] },
{ id: 'b', elems: ['x', 't', 'u', 'i', 'z'] },
{ id: 'c', elems: ['m', 'n', 'o', 't'] },
];
// get number of occurences for each elem
const elems
= y.flatMap(e => e.elems).reduce((acc,elem) => {
acc[elem] = acc[elem] ? acc[elem]+1 : 1;
return acc;
}, {});
// get unique elems
const unique = Object.keys(elems).filter(elem => elems[elem]===1);
// remove non-unique elems from each item
const res = y.map(item =>
({ ...item, elems: item.elems.filter(e => unique.includes(e)) })
);
console.log(res);

Using a Map to track counts after one loop through then use the count of that Map in a filter for final results
let x = ['a', 'b']
let y = [{
id: 'a',
elems: ['x', 'y', 'z']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'o', 't']
},
]
const counts = new Map()
// first iteration to count values
y.forEach(({ elems }) => elems.forEach(v => counts.set(v, (counts.get(v) || 0) + 1)));
// second iteration to filter out dups
y.forEach(e => e.elems = e.elems.filter(v => counts.get(v) === 1))
console.log(y)

Let me know if this works for you.
let y = [
{
id: "a",
elems: ["x", "y", "z", "k"],
},
{
id: "b",
elems: ["x", "t", "u", "i", "z"],
},
{
id: "c",
elems: ["m", "n", "x", "z", "t"],
},
];
// For every element in first array
for (let el of y[0].elems) {
//If we find that every other array includes it
if (y.every((obj) => obj.elems.includes(el))) {
//Remove it from all arrays
for (let obj of y) {
obj.elems = obj.elems.filter((x) => x !== el);
}
}
}

let y = [{
id: 'a',
elems: ['x', 'y', 'z']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'o', 't']
},
];
//
const notExist = (x, arr) => !arr.find(el => el == x);
const restToArrays = (i, arr) => arr.reduce((a, b, index) => index == i ? a : [...a, ...b.elems], []);
const result = y.map((ligne, index, arr) => ({
id: ligne.id,
elems: ligne.elems.filter(v => notExist(v, restToArrays(index, arr)))
}))
console.log(result);

Related

Can't find the longest occurancy in a set of duplicates

The target is to detect superstring in a set of arrays. In this case it should be 'bal' but I get 'lbal'.
const arr = [
['g', 'l', 'o', 'b', 'a', 'l'],
['b','a','l','l']
]
const res = argv.reduce((acc, val) => acc.filter(elem => val.includes(elem)))
This function just gives ALL duplicates(items that are presented in any array) when I need only the most long duplicate sequence. Any suggestions?
you can create an object that counts how many times an element is present in the array of array
like this
const arr = [
['a', 'b', 'm'],
['g', 'o', 'a', 'b'],
['w', 'o', 'u', 'k', 'a', 'b']
]
const countTimes = data => data.flat().reduce((res, v) => {
return {
...res,
[v]: (res[v] || 0 ) + 1
}
}, {})
const duplicates = data => Object.entries(countTimes(data))
.filter(([v, n]) => n > 1)
.map(([v, n]) => v)
console.log(countTimes(arr))
console.log(duplicates(arr) )
const original = [
['a', 'b', 'm'],
['g', 'o', 'a', 'b'],
['w', 'o', 'u', 'k', 'a', 'b']
]
// The easiest is to split up between unique results and duplicates
let uniqueValues = []
let duplicates = []
// Now we're going to loop every array
original.forEach((arr) => {
// Loop every value inside the array
arr.forEach((value) => {
// Check if we had this value already
if (!uniqueValues.includes(value)) {
uniqueValues.push(value)
} else {
duplicates.push(value)
}
})
})
console.log('Duplicates: ', duplicates)
// If you want remove the duplicates from the duplicates, use set
let uniqueDuplicates = [...new Set(duplicates)]
console.log('Unique duplicates: ', uniqueDuplicates)
If you don't have to know in which array duplicates are, you can use Array.prototype.flat() to get rid of nested arrays, then check simple array for duplicates.
const arr = [
['a', 'b', 'm'],
['g', 'o', 'a', 'b'],
['w', 'o', 'u', 'k', 'a', 'b']
]
const arr2 = arr.flat() // ['a', 'b', 'm', 'g', 'o', 'a', 'b', 'w', 'o', 'u', 'k', 'a', 'b']
const hasDuplicates = new Set(arr2).size !== arr2.length
you can create a tally, to find how many each element globally is duplicated by doing a nested forEach
function findDuplicates(data) {
const map = {};
data.forEach((row) => {
row.forEach((item) => {
if (!map[item]) {
map[item] = 1;
} else [map[item]++];
});
});
return map;
}
You can create a simple counter object (if you know python, this is similar to collections.Counter):
class Counter extends Map {
update(values) {
for (let val of values)
this.set(val, 1 + (this.get(val) ?? 0))
}
}
ARR = [
['a', 'b', 'm'],
['g', 'o', 'a', 'b'],
['w', 'o', 'u', 'k', 'a', 'b']
]
const tally = new Counter()
for (let subArray of ARR)
tally.update(new Set(subArray))
for (let [element, count] of tally)
if (count === ARR.length)
console.log(element)
count === yourArrayOfArrays.length selects elements that appear in all arrays, you can replace it with count > 1 to find any duplicates.

JavaScript: Logic traversing an array correct?

Consider the following:
Let's say below is the multi-dimensional, and you are given the starting point of [2, 2].
const WORLD = [
['P', 'P', 'P', 'C', 'P'],
['P', 'M', 'P', 'C', 'P'],
['P', 'M', 'P', 'C', 'P'], <---- That would result the "P" at the 2nd index
['P', 'M', 'P', 'P', 'P'],
['P', 'M', 'P', 'P', 'P']
];
Each of those values represent a terrain type described separately:
const TERRAIN_TYPES = {
'P': {
obstacle: false,
description: 'plains'
},
'M': {
obstacle: true,
description: 'mountains'
},
'C': {
obstacle: true,
description: 'crevasse'
}
};
My question is this... At first blush when I see a multi dimensional array, I imagine some kind of dynamic algorithm one should be using to traverse through it. But the fact a starting point, and a legend regarding the terrain types is given!! So my question is the following implementation brute force?
(I have a function excerpt from a class which accepts some commands. Also I included the commands and directions this function can use!)
const DIRECTIONS = ['N', 'S', 'E', 'W'];
const COMMANDS = ['L', 'R', 'F', 'B'];
executeCommads(commands) {
const LEFTCOMMAND = COMMANDS[0];
const RIGHTCOMMAND = COMMANDS[1];
const FORWARDCOMMAND = COMMANDS[2];
const BACKWARDCOMMAND = COMMANDS[COMMANDS.length - 1]
const loc = this.location
const moveForward = this.location[1] - 1;
const moveBackWard = this.location[1] + 1;
const positionPossible = !this.positionPossible(WORLD[loc[0]][loc[1]])
for (var i = 0; i < commands.length; i++) {
if (commands[i] === LEFTCOMMAND) {
this.direction = 'W'
}
if (commands[i] === RIGHTCOMMAND) {
this.direction = 'E'
}
if (commands[i] === FORWARDCOMMAND) {
if (positionPossible) {
this.location[1] = moveForward
break
}
}
if (commands[i] === BACKWARDCOMMAND) {
if (positionPossible) {
this.location[1] = moveBackWard
break
}
}
}
return {
status: STATUS_CODES[0],
loc: this.location,
dir: this.direction
}
}
The part in question is this:
const moveForward = this.location[1] - 1;
const moveBackWard = this.location[1] + 1;
const positionPossible = !this.positionPossible(WORLD[loc[0]][loc[1]])
I would think that makes a lot of sense, that function:
positionPossible(value) {
return TERRAIN_TYPES[value].obstacle
}
checks the WORLD[2][1] if that position/terrain is a plain type, and then executes the COMMAND value passed i.e. F from the possible ['L', 'R', 'F', 'B']
For 'F' it will be const moveForward = this.location[1] - 1;
Thus resulting in:
const WORLD = [
['P', 'P', 'P', 'C', 'P'],
['P', 'M', 'P', 'C', 'P'], <---- That would result the "P" at the 2nd index
['P', 'M', 'P', 'C', 'P'],
['P', 'M', 'P', 'P', 'P'],
['P', 'M', 'P', 'P', 'P']
];
Hopefully that all makes sense, just wondering if that is kosher?
Thanks in advance!

JavaScript Algorithms question: how do I use recursion to solve such a combination problem?

we have a digit letter map that looks like this
const digitsLetters = new Map([
["2", ['a', 'b', 'c']],
["3", ['d', 'e', 'f']],
["4", ['g', 'h', 'i']],
["5", ['j', 'k', 'l']],
["6", ['m', 'n', 'o']],
["7", ['p', 'q', 'r', 's']],
["8", ['t', 'u', 'v']],
["9", ['w', 'x', 'y', 'z']],
]);
The question asks us to return the all possible letter combinations that the number could represent. For example, if we have "23" then first digit 2 maps to ['a', 'b', 'c'] and second digit maps to ['d', 'e', 'f'] and we end up getting ["ad","ae","af","bd","be","bf","cd","ce","cf"].
I have found a way to produce such a combination between two arrays.
// this will output ["ad","ae","af","bd","be","bf","cd","ce","cf"]
['a', 'b', 'c'].map(char1 => ['d', 'e', 'f'].map(char2 => char1 + char2)).flat(2)
So I thought I could just recursively apply this algorithm for such digit until I hit the last one. I think it is doable. However I had a hard time implementing the solution. Can someone please help me?
Let's consider the cartesian product f(E, F) with an example
assume E is like ['12', '13']
then you get candidates a and b, which I name as F = ['a', 'b']
f(E, F) = E x F would give ['12a', '13a', '12b', '13b']
(note that ExF is the same type of E, an array of string)
now you can recurse on the digits given
g([digit, ...otherDigits], E) => {
candidates = m.get(digit)
EF = cartesianProduct(E, candidates)
return g(otherDigits, EF)
}
Note that the initialization on E should not be the empty array, but an array of length 1 whose sole element is the "neutral" element (empty string for strings)
const data = new Map([
["2", ['a', 'b', 'c']],
["3", ['d', 'e', 'f']],
["4", ['g', 'h', 'i']],
["5", ['j', 'k', 'l']],
["6", ['m', 'n', 'o']],
["7", ['p', 'q', 'r', 's']],
["8", ['t', 'u', 'v']],
["9", ['w', 'x', 'y', 'z']],
]);
const f = (E, F) => E.flatMap(e => F.map(f => e + f))
const g = ([digit, ...otherDigits], E=['']) => digit ? g(otherDigits, f(E, data.get(digit))) : E
console.log(g('23'.split('')))
console.log(g('234'.split('')))
You can basically just use map method to get array from you Map data based on string param and them implement Cartesian product algo.
const data = new Map([
["2", ['a', 'b', 'c']],
["3", ['d', 'e', 'f']],
["4", ['g', 'h', 'i']],
["5", ['j', 'k', 'l']],
["6", ['m', 'n', 'o']],
["7", ['p', 'q', 'r', 's']],
["8", ['t', 'u', 'v']],
["9", ['w', 'x', 'y', 'z']],
]);
function f(str, map) {
const result = [];
const arr = str.split('').map(c => map.get(c))
function r(data, n = 0, prev = []) {
if (n === data.length) {
return result.push(prev.slice())
}
for (let i = 0; i < data[n].length; i++) {
prev[n] = data[n][i]
r(data, n + 1, prev.slice())
}
}
r(arr)
return result;
}
console.log(f('23', data))
console.log(f('358', data))
Something like this
const digitsLetters = new Map([
["2", ['a', 'b', 'c']],
["3", ['d', 'e', 'f']],
["4", ['g', 'h', 'i']],
["5", ['j', 'k', 'l']],
["6", ['m', 'n', 'o']],
["7", ['p', 'q', 'r', 's']],
["8", ['t', 'u', 'v']],
["9", ['w', 'x', 'y', 'z']],
]);
const foo = (arr, result = []) => {
if (arr.length === 0) return result;
const value = arr.shift();
if (result.length === 0) return foo(arr, value);
const newResult = [];
result.forEach((el) => {
value.forEach((el2) => {
newResult.push(el + el2);
});
});
return foo(arr, newResult);
};
const boo = (str) => foo(str.split('').map((symbol) => (digitsLetters.get(symbol))));
console.log(boo(''));
console.log(boo('2'));
console.log(boo('23'));
console.log(boo('232'));

How to kill duplicates in array without using Set?

How can i delete all duplicates in array without using Set (because it will disturb the order)
And why my loop with splice method doesnt work?
let result = [
'j', 'a', 'a', 'v',
'a', 'a', 's', 'c',
'r', 'i', 'p', 't'
]
;
for (let i = 0; i < result.length; i++) {
if (result[i] === result[i + 1]){
result.splice(result.indexOf(result[i]), 1);
}
}
console.log(result) //[ "j", "v", "a", "a", "s", "c", "r", "i", "p", "t" ]
expected output - > [ "j", "a", "v", "a", "s", "c", "r", "i", "p", "t" ]
You could filter the array and have a look to the predecessor.
const
data = ['j', 'a', 'a', 'v', 'a', 'a', 's', 'c', 'r', 'i', 'p', 't'],
result = data.filter((v, i, a) => v !== a[i - 1]);
console.log(...result);
Your method fails in your condition:
if (result[i] === result[i + 1])
Because you only check to see if the next item is the same as the current item and not against the entire array.
To solve this you can create a new array and push in this array only the values that are not already there, thus eliminating duplicates.
const result2 = [];
for (let i = 0; i < result.length; i += 1) {
if (result2.indexOf(result[i])>-1) {
continue;
}
result2.push(result[i]);
}
I think to remove duplicate from an array write a function that do the job and return the array with unique item in it. Please find the below code that serve the propose.
function removeDuplicate(data) {
const tempArray = [];
data.forEach( (item) => {
if (tempArray.indexOf(item) === -1) {
tempArray.push(item);
}
});
return tempArray;
}
let result = [
'j', 'a', 'a', 'v',
'a', 'a', 's', 'c',
'r', 'i', 'p', 't'
];
result = removeDuplicate(result );

Convert multiple arrays to single JSON

I need help converting several arrays:
x = ['a', 'b', 'c']
y = ['d', 'e', 'f']
z = ['d', 'g', 'h']
Into a single JSON:
{
a: { b: { c: 'done' }},
d: { e: { f: 'done' },
g: { h: 'done' }}
}
Is this possible using recursion? I can't seem to get it working properly so I was wondering if there is already an easy way to do this in JS.
var struct = {};
var paths = [
['a', 'b', 'c'],
['d', 'e', 'f'],
['d', 'g', 'h']
];
paths.forEach(function (path) {
var ref;
ref = struct;
path.forEach(function (elem, index) {
if (!ref[elem]) {
ref[elem] = index === path.length - 1 ? "done": {};
}
ref = ref[elem];
});
});
console.log(JSON.stringify(struct, null, "\t"));
Output:
{
"a": {
"b": {
"c": "done"
}
},
"d": {
"e": {
"f": "done"
},
"g": {
"h": "done"
}
}
}
Note: this script will fail if your input is like:
var paths = [
['a', 'b', 'c'],
['a', 'b', 'c', 'd' ]
];
It decided c should be "done" but then there is another level. maybe this wont ever happen, if it does, figure out what you want the result to be.
A version with Array#forEach() and Array#reduce()
var x = ['a', 'b', 'c'],
y = ['d', 'e', 'f'],
z = ['d', 'g', 'h'],
object = function (array) {
var o = {};
array.forEach(function (a) {
var last = a.pop();
a.reduce(function (r, b) {
r[b] = r[b] || {};
return r[b];
}, o)[last] = 'done';
});
return o;
}([x, y, z]);
document.write('<pre>' + JSON.stringify(object, 0, 4) + '</pre>');

Categories

Resources