Javascript - Delete all duplicates from array - javascript

I have problem with delete all duplicate in array.
Array = [1,1,2,2,3]
Every solution, what I found, haves result this
Array = [1,2,3]
But I need this
Array = [3]
How can I do this?

You can first iterate over the array once to obtain a Map of the frequencies of each item and then filter to find the elements that only appeared once.
const arr = [1,1,2,2,3];
const freq = arr.reduce((acc,curr)=>(acc.set(curr,(acc.get(curr)||0)+1),acc),new Map);
const res = arr.filter(x => freq.get(x) === 1);
console.log(res);

You could store an object for occurences of each element and get the elements that have the occurence of 1
const arr = [1, 1, 2, 2, 3]
const occurrences = arr.reduce((acc, el) => {
acc[el] = (acc[el] || 0) + 1
return acc
}, {})
const res = Object.entries(occurrences)
.filter(([el, time]) => time === 1)
.map(([el]) => +el)
console.log(res)

Unlike some of the other solutions, this allows you to make a single loop over the array, rather than a reduce followed by a filter and/or map loop. That said, there are trade-offs in readability and other condition checks, and it plays a bit fast and loose with the semantic intention of a reduce, so it might be a wash in terms of benefits.
const myArray = [1,1,2,2,3];
const dupesRemoved = myArray.reduce((acc, cur, idx, src) => {
if (!acc.dupes.has(cur)) {
if (acc.singleInstances.has(cur)) {
acc.singleInstances.delete(cur);
acc.dupes.add(cur);
} else {
acc.singleInstances.add(cur);
}
}
if (idx === src.length - 1) {
return [...acc.singleInstances];
}
return acc;
}, { singleInstances: new Set(), dupes: new Set() });
console.log(dupesRemoved);

Here is a simple and short solution:
let arr = [1,1,2,2,3];
let filtered_arr = arr.filter(v => arr.indexOf(v) === arr.lastIndexOf(v));
console.log(filtered_arr);

Related

Find Missing levels and fill it

I have two arrays and need to fill the missing values with NA by comparing the levels present in other array. I used the arr.find to search but not sure how to proceed further.
Input:
const levels = [1,2,3,4]
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}]
Output:
out = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":2,"NAME1":"NA"},{"LEVEL":3,"NAME1":"TOM"},{"LEVEL":4,"NAME1":"NA"}]
Code:
let presentLevels = [];
for (let i = 1; i <= levels.length; i++) {
let check = arr.find(p => p['LEVEL'] === levels[i])
if (check) {
presentLevels.push(i)
}
}
console.log(presentLevels)
You can use map() the levels array. Find the object with LEVEL equal to the current element. If you find an object then just return that otherwise return a new object with LEVEL and NAME1 props
const levels = [1,2,3,4]
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}]
const res = levels.map(x => (arr.find(a => a.LEVEL === x) || {level: x, NAME1: "NA"}));
console.log(res)
Using Array.find() inside a loop might cause a performance issue if the arr is large enough. I would create a Map of existing levels by LEVEL, and then use the Map to get the existing levels.
Since you want the presentLevels array to be ordered by the number of the level, you'll need to iterate the levels array, and return a new array. You can do this easily with Array.map(). On each iteration take the current level from the existing Map, and if not found in existing return a new object with NA.
const levels = [1, 2, 3, 4]
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}]
const existingMap = new Map(arr.map(o => [o.LEVEL, o]))
const presentLevels = levels.map(LEVEL =>
existingMap.get(LEVEL) || { LEVEL, NAME1: 'NA' }
);
console.log(presentLevels)
You can make a loop with levels to get the items which arr doesn't have, then adding that items to arr
const levels = [1,2,3,4]
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}]
var items = levels.filter(level => !arr.find(item => item.LEVEL === level));
items.forEach(level => arr.push({LEVEL: level, NAME1: "NA"}));
console.log(arr.sort((a, b) => a.LEVEL - b.LEVEL));
You could first map the levels to the object array with all NA, and then iterate arr to replace those where necessary:
const levels = [1,2,3,4];
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}];
const result = levels.map(LEVEL => ({LEVEL, NAME1: "NA"}) );
for (let o of arr) result[o.LEVEL-1] = o;
console.log(result);
Although this executes two loops, they are not nested, and so this task is performed with linear time complexity (contrary to solutions that have a find call inside the loop).
maybe like this:
const levels = [1,2,3,4];
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}];
for(var key_l in levels){
var found_levels = false;
for(var key_ar in arr){
if(arr[key_ar].LEVEL == levels[key_l]){
found_levels = true;
}
}
if(!found_levels){
arr.push({"LEVEL":levels[key_l],"NAME1":"NA"});
}
}
/* for result sorting, if need... */
arr.sort(function(a, b){
return a.LEVEL > b.LEVEL;
});
console.log(arr);

in the easiest and most concise way as possible

I want to sort an array values in an ascending or descending order without using sort().
I have created a function, however I am not satisfied with it.
I believe the code below could be much shorter and more concise.
Please let me know where to modify or you may entirely change the code too. Thank you in advance.
const func = arg => {
let flip = false;
let copy = [];
for(let val of arg) copy[copy.length] = val;
for(let i=0; i<arg.length; i++) {
const previous = arg[i-1];
const current = arg[i];
if(previous > current) {
flip = true;
copy[i] = previous;
copy[i-1] = current;
}
}
if(flip) return func(copy);
return copy;
};
l(func([5,2,8,1,9,4,7,3,6]));
If your input is composed of whole numbers, as in the example, pne option is to reduce the array into an object, whose keys are the numbers, and whose values are the number of times those values have occured so far. Then, iterate over the object (whose Object.entries will iterate in ascending numeric key order, for whole number keys), and create the array to return:
const func = arr => {
const valuesObj = {};
arr.forEach((num) => {
valuesObj[num] = (valuesObj[num] || 0) + 1;
});
return Object.entries(valuesObj)
.flatMap(
([num, count]) => Array(count).fill(num)
);
};
console.log(
func([5,2,8,1,9,10,10,11,4,7,3,6])
);
This runs in O(N) time.
To account for negative integers as well while keeping O(N) runtime, create another object for negatives:
const func = arr => {
const valuesObj = {};
const negativeValuesObj = {};
arr.forEach((num) => {
if (num >= 0) valuesObj[num] = (valuesObj[num] || 0) + 1;
else negativeValuesObj[-num] = (negativeValuesObj[-num] || 0) + 1;
});
return [
...Object.entries(negativeValuesObj).reverse()
.flatMap(
([num, count]) => Array(count).fill(-num)
),
...Object.entries(valuesObj)
.flatMap(
([num, count]) => Array(count).fill(num)
)
];
};
console.log(
func([5,2,8,1,-5, -1, 9,10,10,11,4,7,3,6, -10])
);
For non-integer items, you'll have to use a different algorithm with higher computational complexity.

How to remove certain elements from an array into a new array and leave the others only the original array?

How to write a function to remove certain elements into a new array and leave the original array with only the remaining elements?
the first part is easy using a for loop pushing the even numbers into a new array but mutating the original array to leave only the odd numbers is hard
function remove(arr, cb){
var removed = [];
var newArr = [];
for(var i = 0; i < arr.length; i++) {
if(cb(arr[i], i, arr)) {
removed.push(arr[i]);
}
}
return removed;
}
Use an else statement to fill newArr with values that should stay in the original arr, then empty it using splice() before copying the items from newArr back into it.
function remove (arr, cb) {
var removed = [];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (cb(arr[i], i, arr)) {
removed.push(arr[i]);
} else {
newArr.push(arr[i]);
}
}
arr.splice(0);
for (var i = 0; i < newArr.length; i++) {
arr.push(newArr[i]);
}
return removed;
}
Welcome to Stackoverflow!
Personally, I'd avoid anything that mutates an input parameter, as this increases code complexity and makes it hard to reason about what's happening from the calling side.
Instead, I'd write a method that returns an array of two arrays. This can be easily split into two variables at the calling end using by using array destructuring.
See the example below:
const splitArr = (arr, pred) =>
arr.reduce(
(prev, curr, idx) => {
prev[+pred(curr, idx, arr)].push(curr);
return prev;
}, [[], []]
);
// usage //
const myArr = [1, 2, 3, 4];
const [arr1, arr2] = splitArr(myArr, x => x > 2);
console.log(arr1);
console.log(arr2);
Because pred is a function that returns a boolean value, we can co-erce this value to 0 or 1 using +someBoolean. We can then use this value as an index to decide into which of the two output arrays the value should be pushed.
You were definitely on the right track with your solution, a couple tweaks and we can make it very readable and also very easy to work with. I tried to keep the format of what it looked like you were doing.
I do take advantage of destructuring here, this could be returned as just an object, and then reference the properties.
const myArr = [0,1,2,3,4,5,6,7,8,9,10];
const splitItems = (arr, logicFunc) => {
let secondSet = []
const firstSet = arr.filter(v => {
if (logicFunc(v)) return true
else secondSet.push(v)
})
return { firstSet, secondSet }
}
const myLogicFunc = v => (v < 3 || v == 9)
const { firstSet, secondSet } = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${firstSet}`) // My first set: 0,1,2,9
console.log(`My second set: ${secondSet}`) // My second set: 3,4,5,6,7,8,10
/* OR without destructuring:
const myArrays = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${myArrays.firstSet}`)
console.log(`My second set: ${myArrays.secondSet}`)
*/
Please let me know if you have any questions
In modern JavaScript apps we do not mutate arrays we create new array, this avoids side effects, so what we do is create two new arrays
const split = (source, conditionFunc) = [ source.filter(i => conditionFunc(i)), source.filter(i => !conditionFunc(i))];
Then you have an array of two arrays of the values that meed condition and those that don't and you have not caused any side effects.
const odssAndEvens = split(source, i => i % 2 === 1);
Or with reduce so you don't iterate the array twice
const split = (source, conditionFunc) = source.reduce((results, item) => {
if (conditionFunc(item)) {
results[0].push(item);
} else {
results[1].push(item);
}
return results;
}, [[],[]]);

JavaScript array: is there a method that is a cross between splice and filter?

I have an array of objects, and want to:
Remove certain objects from the array
Treat the removed objects in a second step
I don't know in advance where these objects are. To recognize them, I need to use a function that queries their properties. It makes sense to retrieve the removed objects in a second array.
I had hoped to find a native method like filter or splice that would do this. Here's what I've come up with as a solution:
if (!Array.prototype.cherrypick) {
Array.prototype.cherrypick = function(fn) {
let basket = []
let ii = this.length
let item
for ( ; ii-- ; ) {
item = this[ii]
if (fn(item)) {
basket.unshift(item)
this.splice(ii, 1)
}
}
return basket
}
}
Have I missed something? Is there a native method that does this already? Is my solution unsound in some way?
Have I missed something? Is there a native method that does this already?
No, most native utility methods try not to mutate the array and instead return a new one.
Is my solution unsound in some way?
Using splice and unshift repeatedly like you do is very inefficient. Better write
if (typeof Array.prototype.cherrypick == "function")
console.warn("something already defines Array#cherrypick!");
Array.prototype.cherrypick = function(predicate) {
let removed = [];
for (let i=0, j=0; i<this.length; i++) {
const item = this[i];
if (fn(item)) {
removed.push(item);
} else {
this[j++] = item; // keep in array, but at new position
}
}
this.length = j; // removes rest
return removed;
};
Methods such as Array.filter() returns a new array instead of changing the original array.
You can create a partition method using Array.reduce() that will return two arrays - those that passed the predicate, and those that failed:
const partition = (predicate, arr) =>
arr.reduce((r, o) => {
r[+!!predicate(o)].push(o);
return r;
}, [[], []]);
const arr = [4, 8, 3, 10, 12];
const result = partition(n => n > 5, arr);
console.log(result);
And you can use the partition logic with Array.splice() to create the cherrypick method:
if (!Array.prototype.cherrypick) {
Array.prototype.cherrypick = function(predicate) {
const [removedItems, items] = arr.reduce((r, o) => {
r[+!!predicate(o)].push(o);
return r;
}, [[], []]);
this.splice(0, arr.length, items);
return removedItems;
}
}
const arr = [4, 8, 3, 10, 12];
const removed = arr.cherrypick(n => n > 5);
console.log('arr ', arr);
console.log('removed ', removed);
Just filter twice:
const picked = array.filter(fn);
array = array.filter((el, i, a) => !fn(el, i, a));
Use reduce as follows :
array = [1,2,3,4,5,6,7];
fn = n => n % 3 == 0;
[array, picked] = array.reduce ( (r, el) => (r[+fn(el)].push (el), r), [[], []] )
Do you want something like this?
const basket = ['apple', 'banana', 'car'];
const filterMapBasket = basket
.filter(item => item !== 'car')
.map(item => {return { name: item }});
This will result the initial basket array of strings to be filtered and transformed to an array of objects.
This will alter the source array in place removing items meeting some test, and return those items...
Array.prototype.removeIf = function(fn) {
let i = this.length;
let removed = [];
while (i--) {
if (fn(this[i], i)) {
removed.push(...this.splice(i, 1));
}
}
return removed;
};
let a = [0,1,2,3,4,5];
let removed = a.removeIf(i => i%2);
console.log(a);
console.log(removed);

Return unique elements from two arrays

I have two arrays of numbers I want get get the unique numbers that appears in both arrays. Then I want to also return the unique numbers from both arrays.
For example:
INPUT:
let arr1 = [1234,4056,3045]
let arr2 = [5678,1234,5001]
OUTPUT:
only in arr1: [4056, 3045]
only in arr2: [5678, 5001]
in both lists: [1234]
Here is my solution, it works but I can't think of how optimize my solution. Just using JavaScript, no tools like loadash. Any thoughts?:
const getUniqueNumbers = (arr1, arr2) => {
let uniqueOfBoth = arr1.filter((ele) => {
return arr2.indexOf(ele) !== -1
})
let uniqueOfList1 = arr1.filter((ele) => {
return arr2.indexOf(ele) == -1
})
let uniqueOfList2 = arr2.filter((ele) => {
return arr1.indexOf(ele) == -1
})
return `Unique numbers from both list are ${uniqueOfBoth}
Unique nums to List1 : ${uniqueOfList1}
Unique nums to List2 : ${uniqueOfList2}
`
}
let result = getUniqueNumbers([1234, 4056, 3045], [5678, 1234, 5001])
console.log(result)
I think this approach is fine so long as it doesn't become a bottle neck. You are doing three O(n**2) operations to get your lists, so it could be nice if there was a way to reduce the complexity.
One thing you could try is to use a hash table that keeps count of how many times the numbers are seen. But you need to be a little clever because you can't just count otherwise you wouldn't know if 1 means arr1 or arr2. But since there are only 4 possibilities you only need 2 bits to represent them. So you add 1 when it's in array1 and 2 when it's in array1. That means 1 in is arr1, 2 in arr2, and 3 is in both. Creating the counts is only O(n+m) where n and m are the array lengths. (You still need to filter that, however, to get your final result)
const getUniqueNumbers =(arr1,arr2) =>{
let counter = {}
arr1.forEach(i => counter[i] = counter[i] ? counter[i] + 1 : 1)
arr2.forEach(i => counter[i] = counter[i] ? counter[i] + 2 : 2)
return counter
}
let counts = getUniqueNumbers([1234,4056,3045],[5678,1234,5001])
console.log(counts)
Then it's just a matter of filtering what you want with something like:
let both = Object.keys(counter).filter(key => result[key] === 3)
You could use Array#includes instead of Array#indexOf, because it returns a boolean value instead of the index.
For getting a difference, you could filter by the unique values of both arrays (this yields a smaller set, than to take the original arrays).
const getUniqueNumbers = (arr1, arr2) => {
let uniqueOfBoth = arr1.filter(ele => arr2.includes(ele))
let uniqueOfList1 = arr1.filter((ele) => !uniqueOfBoth.includes(ele))
let uniqueOfList2 = arr2.filter((ele) => !uniqueOfBoth.includes(ele))
return `Unique numbers from both list are ${uniqueOfBoth}\nUnique nums to List1 : ${uniqueOfList1}\nUnique nums to List2 : ${uniqueOfList2}`
}
let result = getUniqueNumbers([1234, 4056, 3045], [5678, 1234, 5001])
console.log(result)
Here's another version.
This solution assumes the arrays are of equal length. We first iterate through the arrays and store the values in 2 dictionaries. This eliminates any duplicate integers found in the same array. We then iterate over one of the dictionaries and check if the key is found in both, then delete that key from both. Finally, we get the remaining keys from both dictionaries and store them as arrays.
const fn = (arr1, arr2) => {
const obj = {
arr1: [],
arr2: [],
both: []
};
const dict1 = {};
const dict2 = {};
for (let i = arr1.length; i--;) {
dict1[arr1[i]] = true;
dict2[arr2[i]] = true;
}
for (let key in dict1) {
if (key in dict2) {
obj.both.push(key);
delete dict1[key];
delete dict2[key];
}
}
obj.arr1 = Object.keys(dict1);
obj.arr2 = Object.keys(dict2);
return obj;
}
const arr1 = [1234, 4056, 3045];
const arr2 = [5678, 1234, 5001];
console.log(fn(arr1, arr2));

Categories

Resources