Write a function getDuplicates - javascript

Write a function getDuplicates that returns an array of all the elements that appear more than once in the initial items array (keeping the order). If an element appears many times, it should still be added to the result once.
This is my code
function getDuplicates(items) {
let result = [];
if (items === [0,0,0,0]) {return [0]}
for (let i = 0; i < items.length; i++) {
for (let j = i + 1; j < items.length; j++) {
if (items[i] === items[j]) {
result.push(items[i])
}
}
}
return result
}
I get an error:
input: [0, 0, 0, 0]
Hide details
Expected:
[0]
Received:
[0,0,0,0,0,0]

In JavaScript, arrays are objects, so when you use the === operator to compare two arrays, it will only return true if they are the exact same object in memory.
Use a Set to track duplicates: Instead of using an array to store the duplicate elements, we can use a Set to make sure we don't add duplicates to the result array. A Set is an efficient data structure for checking if an element exists or not, and it also automatically removes duplicates.
Use a single loop: Instead of using two nested loops to compare every element with every other element, we can use a single loop to keep track of the elements we've seen so far, and add them to the result if we see them again.
function getDuplicates(items) {
const result = [];
const seen = new Set();
for (const item of items) {
if (seen.has(item) && !result.includes(item)) {
result.push(item);
} else {
seen.add(item);
}
}
return result;
}
console.log(getDuplicates([0, 1, 0, 1, 2]))

a modified version of yours
function getDuplicates(items) {
let result = [];
let added = {};
for (let i = 0; i < items.length; i++) {
if (!added[items[i]] && items.indexOf(items[i], i + 1) !== -1) {
result.push(items[i]);
added[items[i]] = true;
}
}
return result;
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
or in short doing the same
const getDuplicates = items => items.filter((item, index) => items.indexOf(item) !== index && items.lastIndexOf(item) === index);
console.log(getDuplicates([0, 1, 0, 1, 2]))

The best way to filter out the unique elements in an array is JavaScript Set
You cannot compare two arrays just like array1 === array2 because, Arrays have the type Object and you cannot compare two object just with equal to operator. Objects are not compared based on their values but based on the references of the variables. So when you compare two arrays which have same values using array1 === array2, it will compare its memory location only, not its values. So it will be only false.
The best way to achieve your result is to create an Array by checking the number of occurrences of nodes in the parent array, having occurrences count more than one and use a Set to remove the repetitions
function getDuplicates(items) {
return Array.from(new Set(items.filter(node => items.filter(x => node === x).length > 1)))
}
console.log(getDuplicates([0, 1, 0, 1, 2]))

You can try it:
Check if the current number is duplicated by using filter to check the length of an array.
Check if the result array contains duplicates.
function getDuplicates(items) {
let result = [];
for (let i = 0; i < items.length; i++) {
if ((items.filter(item => item == items[i])).length > 1 && !result.includes(items[i])) {
result.push(items[i]);
}
}
return result;
}
console.log(getDuplicates([0, 0, 0, 0]));

So. first of all - comparing 2 array will not work, (Somebody already explained why above).
Your code doesn't work because of if statement. You're checking if an array doesn't have any value except 0.
Try summing all numbers in the array and check if it's 0.
if(arr.reduce((accum, curr) => return accum += curr) == 0) {
return [0];
}

Your code is close, but there are a few issues that need to be addressed. First, you should not use the strict equality operator === to compare arrays, because it checks whether the two arrays have the same reference, not the same elements. Instead, you can use the JSON.stringify() method to compare the string representations of the arrays.
Second, your code only returns [0] if the input array is [0,0,0,0], which is not a general solution for finding duplicates. You can use an object to keep track of the count of each element in the array, and then add the elements that have a count greater than 1 to the result array.
Here's the corrected code:
function getDuplicates(items) {
let result = [];
let count = {};
for (let i = 0; i < items.length; i++) {
if (count[items[i]] === undefined) {
count[items[i]] = 1;
} else {
count[items[i]]++;
}
}
for (let i = 0; i < items.length; i++) {
if (count[items[i]] > 1 && result.indexOf(items[i]) === -1) {
result.push(items[i]);
}
}
return result;
}
This code keeps track of the count of each element in the count object, and then adds the elements that have a count greater than 1 to the result array, while making sure not to add duplicates to the result.

Related

How to convert the index of a flatten multidimensional array (any depth) to the original index?

For example, suppose I have a multidimensional array:
const a1=[["a",["a","b"]],"b",[["b"],"c"]];
which the flatten from is
const a2=["a","a","b","b","c","c"];
and accessing a2[2] is equivalent to a1[0][1][1], also access a2[3] is equivalent to a1[1].
However I'm not interested in what the element is in such index, instead I want a function that can convert the index of a2 equivalent to in a1, eg:
magicFunction(2,[["a",["a","b"]],"b",[["b"],"c"]])
returns [0,1,1],
also
magicFunction(3,[["a",["a","b"]],"b",[["b"],"c"]])
returns [1]. And the input multidimensional array can be any depth (more than 3 layers). How do I write such magic function?
Note: I know that may be easy to do if I know the array is 2d and the sub-array have same number of elements:
const a1=[["a","a"],["b","b"],["c","c"]];
const magicFunction=function(target,a){
let count=0;
for(let i=0;i<a.length;i++){
for(let j=0;j<a[i].length;j++){
if(count==target){
return [i,j];
}
count++;
}
}
return [];
};
alert(magicFunction(3,a1));
However now I need the function to accept a with n-dimensional array which d is unknown before testing, which I don't know how many layers of for-loop should I write to handle it, also the length of each sub-array may not be the same.
Here's a recursive function that generates the path by traversing the array and counting values until it reaches the n'th value. The function returns either an array of the path (if we reach the n'th value) or the current count (if we don't).
const magicFunction = (index, array, count=0) => {
for (let idx = 0; idx < array.length; idx++) {
if (Array.isArray(array[idx])) {
let res = magicFunction(index, array[idx], count)
if (Array.isArray(res)) return [idx].concat(res)
else count = res
}
else {
if (count == index) return [idx]
count++
}
}
return count
}
for (i = 0; i < 6; i++) {
res = magicFunction(i, [["a",["b","c"]],"d",[["e"],"f"]])
console.log(`${i} : [${res}]`)
}
You can flatten an array into a list of [element path] pairs and fetch paths from that list:
function* flatWithPaths(a, parent=[]) {
for (let [i, e] of a.entries()) {
if (Array.isArray(e))
yield* flatWithPaths(e, parent.concat(i))
else
yield [e, parent.concat(i)]
}
}
//
const a = [["a",["b","c"]],"d",[["e","f"],[[["g","h"]]]]]
let n = 0
for (let [elem, path] of flatWithPaths(a)) {
console.log(n++, elem, path.join())
}

Is there a way to filter an array with specific number of returned items in JS?

For example, I only want to get two items that satisfy the filter condition.
filter() then slice(2) will iterate all items which is really a waste while find() can only return one item.
Is there any way to specify the number of returned items that satisfy the filter condition? except writing for loop by myself.
reduce should do the job, consider the following example
[1, 2, 3, 4].reduce((acc, n) => n > 2 ? [...acc, n] : acc, []) // results [3, 4]
You can just loop and break. You could make a helper method to do this:
const found = [];
for (let i = 0; i < sourceArray.length; i++) {
if (whatever) found.push(sourceArray[i];
if (found.length >= 2) break;
}
I would write a helper function that takes in your array, a count number, and a predicate function, similar to how array.filter works. Return true if the item should be included.
function filterCount(array, count, predicate) {
const elements = [];
for (const el in array) {
if (predicate(el)) {
elements.push(el);
}
if (elements.length >= count) {
break;
}
}
return elements;
}
const test = [1,2,3,4,5,6,7,8,9,10];
const onlyTwo = filterCount(test, 2, (number) => number >= 5);
console.log(onlyTwo)

DailyCodingProblem, find pair in array that matches a given value

I'm fairly new to coding and enlisted for the daily coding problem mailing list and got this question:
Given a list of numbers and a number k, return whether any two numbers
from the list add up to k.
My solution (after some stackoverflow digging) looks like this;
function problemOne_Solve()
{
const k = 17;
const values = [11, 15, 3, 8, 2];
for (i=0; i < values.length; i++) {
if ( values.find( (sum) => { return k-values[i] === sum} ) ) return true;
}
return false;
}
I'm wondering why it works. To me it looks like the part with the fat-arrow function closes the brackets inside the if statements conditional logic. And there is no such brackets after the if statement, which I thought was required.
I was also wondering how i would go about outputting the pair or pairs that sums up to "k," to build further on the solution. I would like to be able to display the pairs on the page for example.
.find takes a callback, which is invoked for every item in the array (or, up until a match is found). The first argument to the callback is the item being iterated over. If a match is found (if the return value from the callback was truthy for any element), the .find returns the item that resulted in a truthy return value.
So, on the first i = 0 iteration, and values[i] is 11, (sum) => { return k-values[i] === sum} will first check whether 17 - 11 === 11, and then whether 17 - 11 === 15, and then whether 17 - 11 = 3, etc.
This condition will generally be fulfilled if two numbers in the array add up to the k, but the algorithm is buggy. For example, an array composed of [1] will check the 1 against itself on the first iteration, adding up to 2:
function problemOne_Solve() {
const k = 2;
const values = [1];
for (i=0; i < values.length; i++) {
if ( values.find( (sum) => { return k-values[i] === sum} ) ) return true;
}
return false;
}
console.log(problemOne_Solve());
That is wrong. Another problem is that .find returns the found value. But, if the array is an array of numbers, the found value may be 0, and 0 is falsey. So the below example should return true because two elements sum up to 0 (0 and 0), but it returns false:
function problemOne_Solve() {
const k = 0;
const values = [0, 0];
for (i=0; i < values.length; i++) {
if ( values.find( (sum) => { return k-values[i] === sum} ) ) return true;
}
return false;
}
console.log(problemOne_Solve());
To get it right and decrease the computational complexity from O(n ^ 2) to O(n), iterate over the array once. Create an object whose keys are the numbers being iterated over, and on each iteration, check to see if a key of target - currNum exists on the object (where target is the target sum, and currNum is the current number from the array):
function problemOne_Solve() {
const target = 17;
const values = [11, 15, 3, 8, 2];
const obj = {};
for (const currNum of values) {
if (obj.hasOwnProperty(target - currNum)) {
return true;
}
obj[currNum] = true;
}
return false;
}
console.log(problemOne_Solve());
I was also wondering how i would go about outputting the pair or pairs that sums up to "k," to build further on the solution. I would like to be able to display the pairs on the page for example.
Instead of returning immediately when a match is found, push to an array and then return that array at the end of the function. Also, instead of setting the object values to true (or false), set them to the number of occurrences the number has been found so far (and decrement the matching number when a match is found):
function problemOne_Solve() {
const target = 17;
const values = [11, 15, 3, 8, 2, 17, 0, 0, 17];
const obj = {};
const matches = [];
for (const currNum of values) {
const otherNum = target - currNum;
if (obj[otherNum]) {
obj[otherNum]--;
matches.push([currNum, otherNum]);
}
obj[currNum] = (obj[currNum] || 0) + 1;
}
return matches;
}
console.log(problemOne_Solve());
And there is no such brackets after the if statement, which I thought was required.
Brackets are not required when there's a single statement after an if (or else if or else), eg:
if (true) console.log('true');
else console.log('this will not log');
And there is no such brackets after the if statement, which I thought was required.
If there is only one statement after if else the brackets becomes optional. Ideally you shouldn't write the if block is one line to make your code clean
As you are a beginner I would recommend you to use simple for loops instead of these fancy methods like find.
You can do that in following steps:
Its clear that you need sum of each element with every other element of array. So you will need a nested loop structure
The outer loop or main loop starts from 0 and loop till end of array.
You need to create a inner loop or nested loop which starts from index after the current index.
In the each iteration of nested loop you need to check if the sum of two elements is equal to requiredSum or not.
function pairWithSum(givenArray, requiredSum){
for(let i = 0; i < givenArray.length; i++){
for(let j = i + 1; j < givenArray.length; j++){
let sum = givenArray[i] + givenArray[j];
if(sum === requiredSum){
return [givenArray[i], givenArray[j]];
}
}
}
return false
}
console.log(pairWithSum([1, 4, 5, 8], 12));
console.log(pairWithSum([1, 4, 5, 8], 15));
I'm wondering why it works
That is because, if expects an expression/ statement to validate.
values.find( (sum) => { return k-values[i] === sum} )
This is a statement and it will be evaluated before and its output will be passed to if for condition.
Now Array.find has a return type: T|<null> where T is any value array is made of. So in second iteration, when values[i] refers to 15, it will return 2.
Now in JS, 2 is a truthy value and hence it goes inside if block. Foe more reference, check All falsey values in JavaScript. Any value that is not in this list will be considered as true.
My Javascript solution using JS object. This solution memories the elements when we go through the array which can be memory expensive. But the complexity will stay as O(n).
const checkTwoSum = (arr, sum) => {
const obj = {};
const found = arr?.find(item => {
const target = sum - item;
if (obj[target]) return true;
else {
obj[item] = 1;
}
});
return !!(found || found === 0);
}

Why can I not concat an array to the result of a filter?

I'm a bit curious why the behaviors are different between the two solutions posted below. In the Failing solution, I've concated the zeros array to what I assume by the time of execution, would be the result array of the filter operation. I'm curious why the result is not the updated concated variant (an array with 0s at the end) and instead if simply the initial output of the filter operation.
Passing:
const moveZeros = function (arr) {
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0).concat(zeros)
return filteredArray;
//returns [1,2,3,0,0,0]
}
Failing:
const moveZeros = function (arr) {
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0);
// shouldnt the line below concat zeros to the filter result?
filteredArray.concat(zeros);
return filteredArray;
//returns [1,2,3]
}
This also passes:
return filteredArray.concat(zeros)
concat() doesn't modify the array in place, it returns a new array. Your failing version doesn't return the new array, it returns the original array.
Array.concat() return the concatenated array instead of the original.
let arr = [1,0,0,2,3,0];
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0);
console.log(filteredArray);
let concatenatedArray = filteredArray.concat(zeros);
console.log(concatenatedArray);
So you need to reassign filteredArray like:
filteredArray = filteredArray.concat(zeros);
From the MDN Web Docs
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
In your passing solution you are assigning the result of a .concat() to a variable, and then returning that variable, whereas in your failing solution, you are returning the original array, because you did not assign the result of filteredArray.concat(zeros) to anything

Delete zero values from Array with JavaScript

I have an array with name "ids" and some values like ['0','567','956','0','34']. Now I need to remove "0" values from this array.
ids.remove ("0"); is not working.
Here's a function that will remove elements of an array with a particular value that won't fail when two consecutive elements have the same value:
function removeElementsWithValue(arr, val) {
var i = arr.length;
while (i--) {
if (arr[i] === val) {
arr.splice(i, 1);
}
}
return arr;
}
var a = [1, 0, 0, 1];
removeElementsWithValue(a, 0);
console.log(a); // [1, 1]
In most browsers (except IE <= 8), you can use the filter() method of Array objects, although be aware that this does return you a new array:
a = a.filter(function(val) {
return val !== 0;
});
Use splice method in javascript. Try this function:
function removeElement(arrayName,arrayElement)
{
for(var i=0; i<arrayName.length;i++ )
{
if(arrayName[i]==arrayElement)
arrayName.splice(i,1);
}
}
Parameters are:
arrayName:- Name of the array.
arrayElement:- Element you want to remove from array
Here's one way to do it:
const array = ['0', '567', '956', '0', '34'];
const filtered = array.filter(Number);
console.log(filtered);
For non-trivial size arrays, it's still vastly quicker to build a new array than splice or filter.
var new_arr = [],
tmp;
for(var i=0, l=old_arr.length; i<l; i++)
{
tmp = old_arr[i];
if( tmp !== '0' )
{
new_arr.push( tmp );
}
}
If you do splice, iterate backwards!
For ES6 best practice standards:
let a = ['0','567','956','0','34'];
a = a.filter(val => val !== "0");
(note that your "id's" are strings inside array, so to check regardless of type you should write "!=")
Below code can solve your problem
for(var i=0; i<ids.length;i++ )
{
if(ids[i]=='0')
ids.splice(i,1);
}
ids.filter(function(x) {return Number(x);});
I believe, the shortest method is
var newList = ['0', '567', '956', '0', '34'].filter(cV => cV != "0")
You could always do,
listWithZeros = ['0', '567', '956', '0', '34']
newList = listWithZeros.filter(cv => cv != "0")
The newList contains your required list.
Explanation
Array.prototype.filter()
This method returns a new array created by filtering out items after testing a conditional function
It takes in one function with possibly 3 parameters.
Syntax:
Array.prototype.filter((currentValue, index, array) => { ... })
The parameters explain themselves.
Read more here.
The easy approach is using splice!!. But there's a problem, every time you remove an element your array size will constantly reduce. So the loop will skip 1 index the array size reduces.
This program will only remove every first zero.
// Wrong approach
let num = [1, 0, 0, 2, 0, 0, 3,];
for(let i=0; i<num.length; i++){
if(num[i]==0)
num.splice(i, 1);
}
console.log(num)
the output will be
[1,0,2,0,3]
So to remove all the zeros you should increase the index if you found the non-zero number.
let i = 0;
while(i<num.length){
if(num[i]==0){
num.splice(i,1);
}
else{
i++;
}
}
But there's a better way. Since changing the size of the array only affects the right side of the array. You can just traverse in reverse and splice.
for(let i=num.length-1; i>=0; i--){
if(num[i]===0)
num.splice(i,1);
}

Categories

Resources