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)
Related
I need to check whether one array contains all of the elements of another array, including the same duplicates. The second array can have extra elements. I'm using every...includes, but it's not catching that the second array doesn't have the right duplicates.
For example:
const arr1 = [1, 2, 2, 3, 5, 5, 6, 6]
const arr2 = [1, 2, 3, 5, 6, 7]
if(arr1.every(elem => arr2.includes(elem))){
return true // should return false because arr2 does not have the same duplicates
}
Thanks!
Edit: arr1 is one of many arrays that I am looping through which are coming out of a graph traversal algorithm, so I'd like to avoid restructuring them into an object to create a dictionary data structure if possible.
Try creating this function:
function containsAll (target, toTest) {
const dictionary = {}
target.forEach(element => {
if (dictionary[element] === undefined) {
dictionary[element] = 1;
return;
}
dictionary[element]++;
});
toTest.forEach(element => {
if (dictionary[element] !== undefined)
dictionary[element]--;
})
for (let key in dictionary) {
if (dictionary[key] > 0) return false;
}
return true;
}
Then invoke it like this:
const arr1 = [1, 2, 2, 3, 5, 5, 6, 6]
const arr2 = [1, 2, 3, 5, 6, 7]
console.log(containsAll(arr1, arr2)) // returns false
const arr1 = [1, 2, 2, 3, 5, 5, 6, 6];
//const arr2 = [1, 2, 3, 5, 6, 7];
const arr2 = [1, 2, 2, 3, 5, 5];
let includesAll1 = true;
let includesAll2 = true;
const checkObj1 = {
};
const checkObj2 = {
};
arr1.forEach((el)=> {
if(checkObj1[el] === undefined) {
checkObj1[el] = 1;
} else {
checkObj1[el]++;
}
});
arr2.forEach((el)=> {
if(checkObj2[el] === undefined) {
checkObj2[el] = 1;
} else {
checkObj2[el]++;
}
});
const check1Keys = Object.keys(checkObj1);
const check2Keys = Object.keys(checkObj2);
if(check1Keys.length > check2Keys.length) {
includesAll2 = false;
check2Keys.forEach((key)=> {
const value1 = checkObj1[key];
const value2 = checkObj2[key];
if(!arr1.includes(parseInt(key)) || value1 != value2) {
includesAll1 = false;
}
});
} else {
includesAll1 = false;
check1Keys.forEach((key)=> {
const value1 = checkObj1[key];
const value2 = checkObj2[key];
console.log(value1, value2, key);
if(!arr2.includes(parseInt(key)) || value1 != value2) {
includesAll2 = false;
}
});
}
console.log(includesAll1);
console.log(includesAll2);
Does this solve your problem?
const arr = [1, 2, 3, 5, 6, 7, 2, 10, 2, 3, 2];
const subArr = [1, 2, 2, 3, 2]
const contains = subArr.every(num => subArr.filter(n => n == num).length <= arr.filter(n => n== num).length);
console.log(contains);
You indicate order does not matter in your comments. That makes this very simple.
Sort both arrays
Check if corresponding elements are equal
consider errors associated with sparse or short arrays
Use .reduce() to boil it down to a single result
So this really comes down to a single statement once the arrays are sorted:
matcher.reduce((acc, value , idx)=>matcher[idx] === test[idx], false);
You also mentioned testing this against many arrays. So the full example below does that for demo purposes.
let toMatch = [1, 2, 2, 3, 5, 5, 6, 6]
let arrayOfArrays = [[1,2],[1, 2, 3, 5, 6, 7, 3, 9, 8, 2, 7],[1, 2, 3, 3, 6, 7],[1, 3, 3, 5, 6, 7],[1, 2, 3, 5, 6, 6], [3,5,2,1,6,2,5,6]];
let toMatchSorted = toMatch.slice().sort();
arrayOfArrays.forEach(arr=>{
let toTestSorted = arr.slice().sort();
let out = toMatchSorted.reduce((acc, value , idx)=>toMatchSorted[idx] === toTestSorted[idx], false);
console.log(`Input: ${arr}, Result: ${out}`);
});
I know there are multiple ways to remove duplicates from arrays in javascript, the one i use is
let originalArray = [1, 2, 3, 4, 1, 2, 3, 4]
let uniqueArray = array => [...new Set(array)]
console.log(uniqueArray) -> [1, 2, 3, 4]
what i want is something similar but instead of removing the duplicates, to replace it with whatever string or number i want, like this
console.log(uniqueArray) -> [1, 2, 3, 4, "-", "-", "-", "-"]
this has to work with any order, like
[1, 2, 3, 3, 4, 5, 7, 1, 6]
result -> [1, 2, 3, "-", 4, 5, 7, "-", 6]
i tested this solution
const arr = [1, 2, 3, 1, 2, 3, 2, 2, 3, 4, 5, 5, 12, 1, 23, 4, 1];
const deleteAndInsert = uniqueList => {
const creds = uniqueList.reduce((acc, val, ind, array) => {
let { count, res } = acc;
if (array.lastIndexOf(val) === ind) {
res.push(val);
} else {
count++;
};
return { res, count };
}, { count: 0, res: [] });
const { res, count } = creds;
return res.concat(" ".repeat(count).split(" "));
};
console.log(deleteAndInsert(arr));
but only adds it at the end of the uniques, and also, only works with numbers
i want it to work with strings too, like dates as an example
["2021-02-22", "2021-02-23", "2021-02-22", "2021-02-28"]
You could still use a Set and check if the value is in the set.
const
unique = array => array.map((s => v => !s.has(v) && s.add(v) ? v : '-')(new Set));
console.log(...unique([1, 2, 3, 4, 1, 2, 3, 4]));
console.log(...unique([1, 2, 3, 3, 4, 5, 7, 1, 6]));
Just create new Array, use 1 set to control which element appeared, if one element appears more than 1, push new one character like '-'
let originalArray = [1, 2, 3, 4, 1, 2, 3, 4];
let newArray = [];
let set = new Set();
for (let i = 0; i < originalArray.length; i++) {
if(!set.has(originalArray[i])) {
newArray.push(originalArray[i]);
set.add(originalArray[i]);
} else {
newArray.push('-');
}
}
console.log(newArray);
You could do it with reduce
const dashDupes = array => array.reduce((acc, e) => {
if(acc.idx[e])
acc.arr.push('-')
else{
acc.arr.push(e);
}
acc.idx[e] = true;
return acc;
},{idx:{},arr:[]}).arr
console.log(...dashDupes([1, 2, 3, 4, 1, 2, 3, 4]))
console.log(...dashDupes([1, 2, 3, 3, 4, 5, 7, 1, 6]))
This is a very simple approach to the problem:
function uniqueReplace(arr, rep) {
let res = [];
for (x of arr) {
res.push(res.includes(x) ? rep : x);
}
return res;
}
console.log(...uniqueReplace([1, 2, 3, 4, 1, 2, 3, 4], '-'));
console.log(...uniqueReplace([1, 2, 3, 3, 4, 5, 7, 1, 6], '-'));
I have the next situation in my react application:
I have a state:
const [arr1, setArr1] = useState([1, 2, 3, 5, 1, 3]);
Bellow i render all items from array on UI like:
arr1.map(i => <li>{i}</li>)
Now i want to remove all items that are equal in the array:
ex:
[1, 2, 3, 5, 1, 3] // should be deleted 1 and 3 result: [1, 2, 5]
[1, 2, 3, 5, 3] // should be deleted 3 result: [1, 2, 5]
Deleting all items also the state should change here arr1.map().
I tried setArr1([new Set(arr1)]), but it does not delete all duplicated values, it delete just one of them.
How to achieve what i described above?
You can count the frequency of number and then just pick the number whose frequency is 1.
const arr = [1, 2, 3, 5, 1, 3],
frequency = arr.reduce((o,v) => (o[v] = (o[v] || 0) + 1, o), {}),
unique = Object.keys(frequency).reduce((r,k) => frequency[k] === 1? [...r, +k]: r, []);
console.log(unique);
Check if the the first index equal to the last index on an element,when they are equals it means it is unique:
let result = []
let arr = [1, 2, 3, 5, 1, 3]
arr.forEach(e => arr.indexOf(e) === arr.lastIndexOf(e)?result.push(e):null)
console.log(result)
You can calculate the frequency and remove the number if the frequency is greater than 1. codesandbox
function removeDuplicates(arr) {
const frequency = {};
arr.forEach((el) => {
frequency[el] = frequency[el] ? ++frequency[el] : 1;
});
const result = [];
for (el in frequency) {
if (frequency[el] === 1) {
result.push(el);
}
}
return result;
}
const arrayWithoutDuplicates = removeDuplicates(arr1);
return (
<ul>
{arrayWithoutDuplicates.map((el) => {
return <li key={el}> {el} </li>;
})}
</ul>
);
Below is a one-liner using filter
const arr = [1, 2, 3, 5, 1, 3];
const unique = arr.filter(x => arr.filter(y => y === x).length < 2)
console.log(unique);
This question already has answers here:
Get all unique values in a JavaScript array (remove duplicates)
(91 answers)
Closed 3 years ago.
I received this question for practice and the wording confused me, as I see 2 results that it might want.
And either way, I'd like to see both solutions.
For example, if I have an array:
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
I'm taking this as wanting the final result as either:
let finalResult = [1, 2, 3, 4, 5, 8, 9, 10];
OR:
let finalResult = [1, 9, 10];
The difference between the two being, one just removes any duplicate numbers and leaves the rest and the second just wants any number that isn't a duplicate.
Either way, I'd like to write two functions that does one of each.
This, given by someone else gives my second solution.
let elems = {},
arr2 = arr.filter(function (e) {
if (elems[e] === undefined) {
elems[e] = true;
return true;
}
return false;
});
console.log(arr2);
I'm not sure about a function for the first one (remove all duplicates).
Using Set and Array.from()
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
console.log(Array.from(new Set(arr)));
Alternate using regex
regex explanation here
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let res = arr
.join(',')
.replace(/(\b,\w+\b)(?=.*\1)/ig, '')
.split(',')
.map(Number);
console.log(res);
Alternate using objects
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let obj = arr.reduce((acc, val) => Object.assign(acc, {
[val]: val
}), {});
console.log(Object.values(obj));
Just use a simple array.filter one-liner:
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let finalResult = arr.filter((e, i, a) => a.indexOf(e) == i).sort(function(a, b){return a - b});
console.log(finalResult);
You could use another filter statement if you wanted the second result:
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let finalResult = arr.filter((e, i, a) => a.filter(f => f == e).length == 1).sort(function(a, b){return a - b});
console.log(finalResult);
For the first part you can use Set() and Spread Syntax to remove duplicates.
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let res = [...new Set(arr)]
console.log(res)
For the second part you can use reduce()
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
//to get the object with count of each number in array.
let obj = arr.reduce((ac,a) => {
//check if number doesnot occur before then set its count to 1
if(!ac[a]) ac[a] = 1;
//if number is already in object increase its count
else ac[a]++;
return ac;
},{})
//Using reduce on all the keys of object means all numbers.
let res = Object.keys(obj).reduce((ac,a) => {
//check if count of current number 'a' is `1` in the above object then add it into array
if(obj[a] === 1) ac.push(+a)
return ac;
},[])
console.log(res)
You can use closure and Map
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
const build = ar => {
const mapObj = ar.reduce((acc, e) => {
acc.has(e) ? acc.set(e, true) : acc.set(e, false)
return acc
}, new Map())
return function(hasDup = true) {
if(hasDup) return [...mapObj.keys()]
else return [...mapObj].filter(([key, val]) => !val).map(([k, v])=> k)
}
}
const getArr = build(arr)
console.log(getArr())
console.log(getArr(false))
You can create both arrays in One Go
let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let unique = new Set();
let repeated = Array.from(arr.reduce((acc, curr) => {
acc.has(curr) ? unique.delete(curr) : acc.add(curr) && unique.add(curr);
return acc;
}, new Set()));
console.log(Array.from(unique))
console.log(repeated)
You can use Array.prototype.reduce() create a hash object where the keys are the numbers in the array and the values are going to be the the repeated occurrence of numbers in the arr array variable..
Then using Object.keys():
Remove all duplicates Object.keys(hash)
Remove all duplicates but filtering with Array.prototype.filter() to get the numbers with only one occurrence
Code:
const arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
const hash = arr.reduce((a, c) => (a[c] = (a[c] || 0) + 1, a), {});
// [1, 2, 3, 4, 5, 8, 9, 10];
const finalResultOne = Object.keys(hash);
// [1, 9, 10];
const finalResultTwo = Object.keys(hash).filter(k => hash[k] === 1);
console.log('finalResultOne:', ...finalResultOne);
console.log('finalResultTwo:', ...finalResultTwo);
You could sort the array before and filter the array by checking only one side for duplicates or both sides.
var array = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10],
result1,
result2;
array.sort((a, b) => a - b);
result1 = array.filter((v, i, a) => a[i - 1] !== v);
result2 = array.filter((v, i, a) => a[i - 1] !== v && a[i + 1] !== v);
console.log(...result1);
console.log(...result2)
As many other have said, the first one is just [...new Set(arr)]
For the second, just filter out those that occur more than once:
const arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
const count = (arr, e) => arr.filter(n => n == e).length
const unique = arr => arr.filter(e => count(arr, e) < 2)
console.log(unique(arr));
var arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
var map = {};
var finalResult = [];
for (var i = 0; i < arr.length; i++) {
if (!map.hasOwnProperty(arr[i])) {
map[arr[i]] = true;
finalResult.push(arr[i]);
}
}
//if you need it sorted otherwise it will be in order
finalResult.sort(function(a, b) {
return a - b
});
console.log(finalResult);
Having an array of numbers setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9] I'd like to sort this set to have the smallest numbers on the end and beginning and the biggest in the center of the sorted set like this sortedSetNumbers = [0, 2, 3, 9, 7, 3, 1, -2].
const setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9];
const result = [0, 2, 3, 9, 7, 3, 1, -2];
function sortNormal(a, b) {
return true; // Please, change this line
}
const sortedSetNumbers = setOfNumbers.sort((a, b) => sortNormal(a, b));
if (sortedSetNumbers === result) {
console.info('Succeeded Normal Distributed');
} else {
console.warn('Failed Normal Distribution');
}
console.log(sortedSetNumbers);
I am sure it is possible to sort these numbers with the method Array.prototype.sort(), but how should this sorting function look like?
EDIT: The solution does not have to be solved with .sort(). That was only an idea.
This might be the most naive way to do it, but isn't it simply left, right, left, right... after sorting?
const input = [0, 3, 3, 2, 7, 1, -2, 9];
const expected = [0, 2, 3, 9, 7, 3, 1, -2];
const sorted = input.slice().sort();
const output = [];
let side = true;
while (sorted.length) {
output[side ? 'unshift' : 'push'](sorted.pop());
side = !side;
}
console.log(expected.join());
console.log(output.join());
Or simply:
const input = [0, 3, 3, 2, 7, 1, -2, 9];
const output = input.slice().sort().reduceRight((acc, val, i) => {
return i % 2 === 0 ? [...acc, val] : [val, ...acc];
}, []);
console.log(output.join());
A slightly different approach is to sort the array ascending.
Get another array of the indices and sort the odds into the first half asending and the even values to the end descending with a inverted butterfly shuffle.
Then map the sorted array by taking the value of the sorted indices.
[-2, 0, 1, 2, 3, 3, 7, 9] // sorted array
[ 1, 3, 5, 7, 6, 4, 2, 0] // sorted indices
[ 0, 2, 3, 9, 7, 3, 1, -2] // rebuild sorted array
var array = [0, 3, 3, 2, 7, 1, -2, 9].sort((a, b) => a - b);
array = Array
.from(array, (_, i) => i)
.sort((a, b) => b % 2 - a % 2 || (a % 2 ? a - b : b - a))
.map(i => array[i]);
console.log(array);
This solution is not really elegant, but it does it's job.
const setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9];
const alternation = alternate();
const sortedSetNumbers = sortNormal(setOfNumbers);
function sortNormal(start) {
const result = [];
const interim = start.sort((a, b) => {
return b - a;
});
interim.map(n => {
if (alternation.next().value) {
result.splice(0, 0, n);
} else {
result.splice(result.length, 0, n);
}
});
return result;
}
function* alternate() {
let i = true;
while (true) {
yield i;
i = !i;
}
}
console.log(sortedSetNumbers);