Counting Duplicate elements in an array: Javascript - javascript

Could someone please look over my code and explain why my return value = 3 when it should be 2? The object has the correct count {1: 2, 2: 3, 3: 1, 4: 1, 5: 1}. Only two values are >= 2.
function countDuplicates(arr) {
let dupNums = {};
let count = 0;
​
for (let i=0; i<arr.length; i++) {
​
if (dupNums[arr[i]] === undefined) {
dupNums[arr[i]] = 0;
}
​
if (dupNums[arr[i]] !== undefined) {
dupNums[arr[i]] += 1;
}
​
if (dupNums[arr[i]] >= 2) {
count++;
}
}
return count;
}
console.log(countDuplicates([1,2,1,3,2,4,5,2]));

You have three 2s and two 1s. Each time a duplicate is found, count gets incremented.
Iterate over the values of the object afterwards instead, and count up the number of values which are >= 2:
function countDuplicates(arr) {
const dupNums = {};
for (const num of arr) {
dupNums[num] = (dupNums[num] || 0) + 1;
};
return Object.values(dupNums)
.filter(num => num >= 2)
.length;
}
console.log(countDuplicates([1, 2, 1, 3, 2, 4, 5, 2]));
or with reduce:
function countDuplicates(arr) {
const dupNums = {};
for (const num of arr) {
dupNums[num] = (dupNums[num] || 0) + 1;
};
return Object.values(dupNums)
.reduce((a, num) => a + (num >= 2), 0)
}
console.log(countDuplicates([1, 2, 1, 3, 2, 4, 5, 2]));

Use one Set to track dup nums and another Set for tracking count.
Following should work for you scenario.
function countDuplicates(arr) {
const dupNums = new Set();
const countSet = new Set();
arr.forEach((num) =>
dupNums.has(num) ? countSet.add(num) : dupNums.add(num)
);
return countSet.size;
}
console.log(countDuplicates([1, 2, 1, 3, 2, 4, 5, 2]));

Related

How can I return the mode that appeared first in an array in Javascript?

I want to create a function that will return the number with highest frequency(mode). For example: if array contains [10, 4, 5, 2, 4] the output should be 4. If there is more than one mode, I want to return the one that appeared in the array first (ie. [10,2,5, 4, 5, 2, 4] should return 2 because it appeared first. If there is no mode, I want to return -1. The array will not be empty. Below is my attempt
function Mode(arr){
if(arr == undefined || arr.length == 0){
return
}
const number_to_frequency = {};
let number_with_overall_highest_frequency = 0;
let overall_highest_frequency = 0;
for(index in arr) {
const number = arr[index];
if(number_to_frequency[number]) {
number_to_frequency[number] += 1;
} else {
number_to_frequency[number] = 1;
}
let updated_frequency = number_to_frequency[number]
if(overall_highest_frequency < updated_frequency) {
number_with_overall_highest_frequency = number;
overall_highest_frequency = updated_frequency;
}
}
if(overall_highest_frequency == 1){
return -1
}
return number_with_overall_highest_frequency;
};
console.log(Mode([10,2,5, 4, 5, 2, 4])) //5
If you only need the first one that its repeated you can try this approach
const checkMode = (array = []) => {
const duplicated = array.find(value => array.filter(_value => value === _value).length > 1)
return duplicated >= 0 ? duplicated: -1
}
checkMode([10,2,5, 4, 5, 2, 4])

Find duplicate pairs in an array

I have an array a = [1,1,1,2,2,3,3,3,4,4,4,6,6,6,7,7]
I want to fetch all the duplicate pair in this array list.
Since there are pairs of 2 and 7 the output should be -
Output: [2, 7]
I tried writing my own logic but I am very weak in that area. Can somebody help?
function getDuplicateArrayElements(arr){
let sorted_arr = arr.slice().sort();
let results = [];
for (let i = 0; i < sorted_arr.length; i++) {
let matchingElementCount = 1;
for (let j = i + 1; j < sorted_arr.length - i; j++) {
if (sorted_arr[j] === sorted_arr[i]) {
++matchingElementCount;
} else {
if(matchingElementCount % 2 === 0) {
results.push(sorted_arr[i]);
}
i = j - 1;
break;
}
}
}
return results; } var a = [1,1,1,2,2,3,3,3,4,6,6,6,7,7]; var duplicateValues= getDuplicateArrayElements(a);
You can achieve your result by using reduce and forEach.
const arr = [1,1,1,1,2,2,3,3,3,4,4,4,6,6,6,7,7];
// Generate a hashmap from the given array for counting the frequency.
const hashMap = arr.reduce((a, c) => {
a[c] = (a[c] || 0) + 1;
return a;
}, {});
const pair = [];
// If the frequency is divided by 2 then push the key of the hashMap into pair array.
Object.entries(hashMap).forEach(([k, v]) => {
if (v % 2 === 0) {
[...Array(Math.floor(v / 2))].forEach(_ => pair.push(k));
}
})
console.log(pair);
You can grab the frequency of each number, and then filter out any which have an odd frequency. You can then .flatMap() the frequencies to an array containing your number for each pair you found like so:
const a = [1,1,1,2,2,3,3,3,4,4,4,6,6,6,7,7];
const freq = a.reduce((m, n) => m.set(n, (m.get(n) || 0) + 1), new Map);
const res = [...freq].filter(([n, count]) => count % 2 == 0).flatMap(([n, c]) => Array(c/2).fill(n));
console.log(res);
This way, if you have four 1s (ie: two pairs of 1s), the filter will pick up on that, allowing you to flat-map the [1, 4] array to an array of [1, 1], which is merged into the larger resulting array.
You could create a helper map and keep the counts of each number as the values and the numbers itself as the keys. After iterating through the array, you only need to find the ones with a count divisible by 2:
var a = [1, 1, 1, 2, 2, 3, 3, 3, 4, 6, 6, 6, 7, 7]
function findDuplicates(arr) {
const map = {};
for (const curr of arr) {
if (!map[curr]) {
map[curr] = 0;
}
map[curr]++;
}
const res = [];
for (const key in map) {
if (map.hasOwnProperty(key) && map[key] % 2 === 0) {
res.push(Number.parseInt(key));
}
}
return res;
}
console.log(findDuplicates(a));
You can first count the occurrence of each numbers and if it is greater than 0 and divisible by 2 then add these to final result else don't
function getDuplicateArrayElements(arr) {
let map = {}
let results = [];
for (let num of arr) {
map[num] = map[num] || 0
map[num]++
}
return Object.keys(map)
.filter(v => map[v] && map[v] % 2 === 0)
.map(v => new Array(map[v]/2).fill(+v))
.flat()
.sort()
}
var a = [1, 1, 1, 2, 2, 3, 3, 3, 4, 6, 6, 6, 7, 7,8,8,8,8];
var duplicateValues = getDuplicateArrayElements(a);
console.log(duplicateValues)
const a = {};
[1,1,1,2,2,3,3,3,4,6,6,6,7,7].forEach(v => {a[v] = a[v] ? a[v] + 1 : 1});
const l = [];
Object.keys(a).forEach(k => !(a[k] % 2) && l.push(k));
Here you go:
function getDuplicateArrayElements(arr){
var dupilcates=arr.filter(x => arr.filter(y=>y==x).length==2);
var found=[];
for(var i=0;i<dupilcates.length;i=i+2)
found.push(dupilcates[i]);
return found;
}
This will give you the desired pairs. with [1, 1, 1, 1, 2, 2, 3, 3, 3, 4, 6, 6, 6, 6, 7, 7] input it will return [1,1,2,6,6,7]:
function getDuplicateArrayElements(arr){
let sorted_arr = arr.slice().sort();
let results = [];
let i = 0;
while (i < sorted_arr.length) {
let counter = 1;
let j = i;
while (sorted_arr[j] === sorted_arr[j+1]) {
counter++;
j++;
}
if (counter%2 == 0) {
results.push(...Array(counter/2).fill(sorted_arr[i]))
}
i += counter;
}
return results;
}
var a = [1, 1, 1, 1, 2, 2, 3, 3, 3, 4, 6, 6, 6, 6, 7, 7];
console.log(getDuplicateArrayElements(a));
Another rather concise solution:
a = [1,1,1,2,2,3,3,3,4,4,4,6,6,6,7,7];
uniques = new Set(a); //filter out duplicates
res = [];
uniques.forEach((key)=>{
if(a.filter(elem => elem === key).length === 2){res.push(key)};
//filter out only the elements which match the key being tested
//if there are 2, push to result
})
Edit: even more concise, but perhaps less efficient:
a = [1,1,1,2,2,3,3,3,4,4,4,6,6,6,7,7];
res = Array.from(new Set(a.filter(elem => a.filter(el => el === elem).length === 2)));
Javascript has awesome JSON object, in my opinion, you can use json as a dictionary;
{ key: _value }.
Loop throw array one times, no sort, no slice
key is array's element value, _value is frequency
var frequencies = {};
for (let i = 0; i < a.length; a++) {
if (result[a[i]] == 'over') continue;
if (result[a[i]] == undefined) { // First times
result[a[i]] = 1
} else if (result[a[i]] == 1) { // Second times
result[a[i]] = 2
} else { // Ignore if > 2
result[a[i]] = 'over'
}
}
// result: {1: "over", 2: 2, 3: "over", 4: "over", 6: "over", 7: 2}
so now pick keys have value equal 2
function getDuplicateArrayElements(numbers: number[]): number[] {
const occurences = new Map<number, number>();
for (let number of numbers) {
if (occurences.has(number)) {
const current = occurences.get(number)!;
occurences.set(number, current + 1);
} else
occurences.set(number, 1)
}
return (
Array
.from(occurences.entries())
.reduce<number[]>(
(accumulator, [key, value]) => {
if (value === 2) {
return accumulator.concat(key)
}
return accumulator
},
[]
)
)
}
const a = [1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 6, 6, 6, 7, 7];
getDuplicateArrayElements(a); // [2, 7]

Using reduce to delete even numbers in an object

I'm given an array of numbers. I have created an object named counts, whose keys are the numbers and values the amount of times those numbers appear in the array. Can't work out how to use reduce to delete the even counts of the numbers.
A = [ 20, 1, -1, 2, -2, 3, 3, 5, 5, 1, 2, 4, 20, 4, -1, -2, 5 ]
n = 5
function findOdd(A) {
let counts = {};
for (let i = 0; i < A.length; i++) {
let num = A[i];
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
//counts -> { '1': 2, '2': 2, '3': 2, '4': 2, '5': 3, '20': 2, '-1': 2, '-2': 2 }
const answer = Object.keys(counts).reduce((object, key) => {
if (key % 2 !== 0) {
object[key] = counts[key];
}
return object;
}, {})
return answer;
Must return the key of the odd count.
SOLUTION:
function findOdd(A) {
const counts = {};
for (let i = 0; i < A.length; i++) {
let num = A[i];
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
Object.keys(counts).forEach(key => {
if(counts[key] % 2 === 0) {
delete counts[key];
}
});
return Number(Object.keys(counts));
}
You can make use of Object.entries to obtain entires, then filter entries whose values are odd, and then reconstruct new Object from those entries using Object.fromEntries:
const countObject = { '1': 2, '2': 2, '3': 2, '4': 2, '5': 3, '20': 2, '-1': 2, '-2': 2 };
const oddEntries = Object.entries(countObject).filter(([key, value]) => value % 2 !== 0);
const oddCountObject = Object.fromEntries(oddEntries)
console.log(oddCountObject)
function findOdd(arr) {
const counts = {}; // `const` declared objects/arrays are not immutable
for(let i = 0; i < arr.length; i++) {
counts[arr[i]] = counts[arr[i]] || 0;
counts[arr[i]]++;
}
Object.keys(counts).forEach(key => {
if(counts[key] % 2 === 0) {
delete counts[key];
}
});
return counts;
}
const array = [1, 2, 3, 4, 5, 6, 1, 1, 4, 5, 4, 9];
// 1:3, 2:1, 3:1, 4:3, 6:1, 9:1
// Does not show a `5` key due to there being an even number of fives in `array`
console.log(findOdd(array));
Yes, I know delete is inefficient, but that shouldn't matter, unless it is a requirement for being quick. I believe you can just set counts[key] = undefined or counts[key] = null, you can see the benchmarks here
I know there are already great answers but as the question said "using reduce" I thought it'd be fun to try doing this using only reduce:
const findOdd = arr => arr.reduce((acc, d, i, list) => {
if (!acc[d]) {
acc[d] = 0;
}
acc[d]++;
if (i === list.length - 1) {
return Object.keys(acc)
.reduce((subAcc, key) => ({
...subAcc,
...(acc[key] % 2 === 0 ? {} : {
[key]: acc[key]
})
}), {})
}
return acc;
}, {})

Group array elements into set of n

I have an array
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
I want to group it into a set of n arrays such that first n elements in result[0] next n elements in result[1] and if any element is remaining it is discarded.
let sampleOutput = [[0, 1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13]] for n = 7;
Here is my code:
function group5(arr, len) {
let result = [];
let loop=parseInt(arr.length/len)
for (let i=0; i<arr.length; i+=len) {
let x = []; let limitReached = false;
for (let j=0; j<len; j++) {
if (arr[i+j]) {
x.push(arr[i+j]);
} else {
limitReached = true;
break;
}
}
if (!limitReached) {
result.push(x);
} else {
break;
}
}
return result;
}
But I am unable to get expected result. I have tried following things.
Map function
Running i loop to arr.len
Checking arr.len % 7
Creating an array for every third element.
This question is not duplicate of Split array into chunks because I have to discard extra elements that can not be grouped into sets of n.
I have to keep the original array Immutable because I am using this on props in a child component. I need a function that does not modify the original array.
It's pretty straigthforward using Array.from
const list = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14];
function chunkMaxLength(arr, chunkSize, maxLength) {
return Array.from({length: maxLength}, () => arr.splice(0,chunkSize));
}
console.log(chunkMaxLength(list, 7, 2));
What about :
function group5(arr, len) {
let chunks = [];
let copy = arr.splice(); // Use a copy to not modifiy the original array
while(copy.length > len) {
chunks.push(copy.splice(0, len));
}
return chunks;
}
You could use a combination of reduce and filter to achieve the expected result. This example gives you a third control over length which makes the code a bit more reuseable.
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
const groupNumber = 7;
const groupCount = 2;
const groupArray = (group, size, length) => group.reduce((accumulator, current, index, original) =>
((index % size) == 0)
? accumulator.concat([original.slice(index, index + size)])
: accumulator, []
).filter((single, index) => index < length)
const test = groupArray(arr, groupNumber, groupCount);
console.log(test);
Step by Step
const groupArray = (group, size, length) => {
// if (index modulus size) equals 0 then concat a group of
// length 'size' as a new entry to the accumulator array and
// return it, else return the accumulator
const reducerFunc = (accumulator, current, index, original) =>
((index % size) == 0)
? accumulator.concat([original.slice(index, index + size)])
: accumulator
// if the current index is greater than the supplied length filter it out
const filterFunc = (single, index) => index < length;
// reduce and filter original group
const result = group.reduce(reducerFunc, []).filter(filterFunc)
return result;
}
Also (apart from the existing approaches) you can have a recursive approach like this
function chunks(a, size, r = [], i = 0) {
let e = i + size;
return e <= a.length ? chunks(a, size, [...r, a.slice(i, e)], e) : r;
}
function chunks(a, size, r = [], i = 0) {
let e = i + size;
return e <= a.length ? chunks(a, size, [...r, a.slice(i, e)], e) : r;
}
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
console.log('Chunk with 3: ', chunks(arr, 3));
console.log('Chunk with 4: ', chunks(arr, 4));
console.log('Chunk with 5: ', chunks(arr, 5));
console.log('Chunk with 6: ', chunks(arr, 6));
console.log('Chunk with 7: ', chunks(arr, 7));
I able to solve the problem with this code
function groupN(n, arr) {
const res = [];
let limit = 0;
while (limit+n <= arr.length) {
res.push(arr.slice(limit, n + limit));
limit += n
}
return res
}
I usually prefer declarative solutions (map, reduce, etc), but in this case I think a for is more understandable:
function groupArray(array, num) {
const group = [];
for (let i = 0; i < array.length; i += num) {
group.push(array.slice(i, i + num));
}
return group;
}

How to find and log the longest repeating serie of numbers in array with javascript

My problem is I can't console.log the repeating after another same numbers in the array. I can find which number how many times appears with this code:
var array = [2, 1, 1, 2, 3, 3, 2, 2, 2, 1],
currentNum = 1,
counter = 0,
item;
for (var i = 0; i < array.length; i++) {
for (var j = i; j < array.length; j++) {
if (array[i] == array[j])
counter++;
if (currentNum < counter) {
currentNum = counter;
item = array[i];
}
}
counter = 0;
}
console.log(item + " ( " + currentNum + " times ) ");
But my real issue is that I want to log the 2, 2, 2 part and I don't know how.
Thank you guys very much in advance!
Following will find the first instance of longest (assuming more than one same length sequence)
The first reduce() creates subarrays for each sequence, the second reduce() checks lengths of those sub arrays returning the first instance of longest sequence
var array = [2, 1, 1, 2, 3, 3, 2, 2, 2, 1];
var res = array.reduce(function(a,c,i){
if(a.length && a[a.length-1][0] ===c){
a[a.length-1].push(c)
}else{
a.push([c]);
}
return a
},[]).reduce(function(a,c){
return c.length > a.length ? c : a;
});
console.log('Sequence length=',res.length);
console.log('Value=', res[0]);
If you expect multiple same length sequences and want to capture all the values that match can modify second reduce something like:
var array = [2, 1, 1, 2, 3, 3, 2, 2, 2, 1, 2, 2, 2, 7, 7, 7];
var res = array.reduce(function(a,c,i){
if(a.length && a[a.length-1][0] ===c){
a[a.length-1].push(c)
}else{
a.push([c]);
}
return a
},[]).reduce(function(a,c){
if(c.length > a.len){
// reset when longer sequence found
a.values = [c[0]];
a.len = c.length;
} else if(c.length === a.len){
// add new value when same length found
a.values.push(c[0]);
}
return a
},{len:0,values:[]});
console.log('Sequence length=', res.len)
console.log('How many times=', res.values.length)
console.log('Values = ', res.values.join() )
Array.prototype.forEach or reduce is better for iteration through an array.
Here is a solution with a single going through array (just 3 additional variables :) )
var array = [2, 1, 1, 2, 3, 3, 2, 2, 2, 1]
var item = array[0], maxTimes = 1, currentTimes = 1
array.reduce(function(prev, current) {
if (prev === current) {
currentTimes = currentTimes + 1
if (currentTimes > maxTimes) {
maxTimes = currentTimes
item = current
}
} else {
currentTimes = 1
}
return current
})
console.log(item, maxTimes)
You could use Array#reduce with an object which holds the temporary result and the actual count of the item.
If an item is equal to the last item, last count is incremented and checked if the count is greater than the temporary count. Then the temporary count is replaced by the actual count.
var array = [2, 1, 1, 2, 3, 3, 2, 2, 2, 1],
count = array.reduce(function (r, a, i, aa) {
if (a === aa[i - 1]) {
r.last.count++;
if (!(r.last.count <= r.result.count)) {
r.result = r.last;
}
} else {
r.last = { value: a, count: 1 };
}
return r;
}, { last: {}, result: {} }).result;
console.log(count);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories