An element of an array doesn't pass a test - javascript

I am writing a function that loops through an array and tests each element for a condition. If it is false, the element is dropped. If it is true, the function returns the rest of the array.
Here is my code:
function dropElements(arr, func) {
// Loop through the array
for(var i = 0; i < arr.length; i++){
// if the current element passes the test, return the rest of the array
if (func(arr[i])){
return arr;
// otherwise remove the element
} else {
arr.shift();
}
}
// if no conditions were met, return empty array
return arr;
}
When the loop reaches i = 1, we have arr[1] = 2.
Since 2 is not >= 3, I do not understand why it is not dropped.
So, why does the following call
dropElements([1,2,3,4], function(n) {return n >= 3; });
return [2,3,4] instead of [3,4] ?
Thanks.

Because when you "shift" it removes the index and the other indexes shift down to fill in the hole. So when you remove the first one, the second index is now one. And since you increment i, you skip the index that was moved down.
So to get around your issue, you would need to reduce i
else {
arr.shift();
i--;
}
Or you can do another solution where you find the index and than just splice the array.

From MDN doc:
The shift() method removes the first element from an array and returns that element. This method changes the length of the array.
In your code this means that you have to sync the index in the loop. You can simply decrease the index by 1
function dropElements(arr, func) {
// Loop through the array
for(var i = 0; i < arr.length; i++){
// if the current element passes the test, return the rest of the array
if (func(arr[i])){
return arr;
// otherwise remove the element
} else {
arr.shift();i--;
}
}
// if no conditions were met, return empty array
return arr;}
This will solve your problem

It has to do with the order in which you are shifting the array. Take into comparison the following:
function dropElements(arr, func) {
if (!Array.isArray(arr) && arr.length == 0) {
return [];
}
while (!func(arr[0]) && arr.length > 0) {
arr.shift();
}
return arr;
}

epascarello's answer should be accepted as the correct one, however here is an updated code block for what you need:
function dropElements(arr, func) {
// Loop through the array
for(var i = 0; i < arr.length; i++){
// if the current element passes the test, remove it from the array
if (func(arr[i])){
return arr;
} else {
arr.shift();
i--;
}
}
// Return the array
return arr;
}
var arr = [1,2,3,4,2];
dropElements(arr, function(num) {
return num >= 3;
});
This outputs [3, 4, 2] (this is because we assume that the array is ordered when we loop through it and return from the function when the test function is satisfied once). If you want to loop on an unordered array, simply remove the return arr in the loop, just note that this will run at O(n) opposed to O(log n)

Related

Write a function getDuplicates

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.

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);
}

Create a function to slice without using slice()

I am trying to write a function called slice that accepts an array and two numbers.
The function should return a new array with the elements starting at the index of the first number and going until the index of the second number.
If a third parameter is not passed to the function, it should slice until the end of the array by default.
If the third parameter is greater than the length of the array, it should slice until the end of the array.
function slice(s, n, m) {
let a = [];
a = s.splice(n, m);
if(m === undefined || m > s.length) {
a = s.splice(n, s.length);
}
return a;
}
let s = [1, 2, 3, 4, 5];
slice(s, 1, 7);
output []
UPDATE:
Thanks for everyone's help; I GOT IT!!! happy dance
function slice(arr, start, end) {
let result = [];
from = Math.max(start, 0);
to = Math.min(end);
if((!end) || (end > arr.length)) {
for(let i = from; i<arr.length; i++) {
result.push(arr[i]);}
} else {
for(let i = from; i<to; i++) {
result.push(arr[i]);
}
}
return result;
}
slice([1, 2, 3, 4, 5], 2, 4);
The main problem is that .splice mutates the array, so on your first call to it you remove a part of the array (or in your case the whole array). Therefore if the code enters the if, and you call .splice a second time, the array is already empty. An if / else would work so that .splice gets only called once.
But that would still then not replicate the behaviour of .slice, as .slice does not mutate the original array. Therefore you rather need a loop, that copies one element after the other:
// if "do" doesn't get passed, initialize with array.length (default parameter)
function slice(array, from, to = array.length) {
// make sure the bounds are within the range
from = Math.max(from, 0);
to = Math.min(to, array.length);
// initialize an array we can copy values into
const result = [];
for(let index = from; index < to; index++) {
// left as an exercise :)
}
return result;
}
Answering this since the OP said the time for the homework has passed.
One way to solve this problem is to have two pointers to second and third arguments. If you have all the arguments given, this is how you should start
start = n
end = m
// if m is greater than length of s or if m is not given
if(m == undefined || m > s.length()){
end = s.length() - 1;
}
then it is a simple for loop from start to end, both inclusive.
int[] result = new int[end-start+1];
for(int i = start; i <= end; i++){
result[j] = s[i];
}
Code may not be syntactically correct but you can fix that.

Find the index of an array element without using built in indexOf function

I am fairly new to JS and have a project to find the index of an array element, without using indexOf built in function. I have tried to search for solutions, but all I get are examples of the aforementioned built in function, which I already know how to use. Can anyone provide a simple example for me to go on?
My mind goes towards something like this, but please note I am new to this and it is an academic exercise that I really want to understand:
var index;
var target = 10;
for(var val in collection){
if(collection[val] === target){
index = val;
}
return index;
}
This attempt is almost correct. You seem to already understand of what is required: loop over the elements of the array until you find a match, and then return the index of that match.
You've made a few mistakes, though. Let's walk through some improvements, step by step.
Your return statement is inside the loop, but outside of the if. You only want to return if you're found a match. Currently, you always return after the first iteration of the loop!
function myIndexOf(collection, target) {
var index;
for(var val in collection){
if(collection[val] === target){
index = val;
return index;
}
}
}
There is no need for a separate index variable. You can return val as soon as you determine it's the correct answer.
function myIndexOf(collection, target) {
for(var val in collection){
if(collection[val] === target){
return val;
}
}
}
You should loop using a numeric for loop, not a for-in loop. for-in loops are not guaranteed to have a set order, so you may not always get the lowest index that is a match. (Also, for-in could match on non-numeric property names, which might not be what you want.)
function myIndexOf(collection, target) {
for(var val=0; val<collection.length; val++){
if(collection[val] === target){
return val;
}
}
}
To act just like indexOf, you could return -1 in the case that you don't find a match.
function myIndexOf(collection, target) {
for(var val=0; val<collection.length; val++){
if(collection[val] === target){
return val;
}
}
return -1;
}
Note: for..in should not be used to iterate over an Array where the
index order is important.
for..in - JavaScript | MDN
var find_index = function(collection, item) {
for (var i = 0; i < collection.length; ++i) {
if (collection[i] === item) {
return i;
}
}
};
find_index([5,4,3,2,1], 5)
When looping through an array you should use a simple for loop from index 0 to Length-1 of your array. Don't use for...in because it can iterate properties of the array that aren't the actual contents of the cells and the order is not guaranteed. You want to find the first match to have the same behavior as .indexOf. So the correct implementation would look something like this:
function myIndexOf(array, target) {
for (var i=0; i < array.length; i++) {
if (array[i] === target) {
return i;
}
}
// item was not found
return -1;
}
You can use in statement to iterate an array, assumings keys are values. Here val will be 0, 1, 2... so you can return it.
Then, you can use the return inside the if : it will stop the function and return the value just right you find what you are loonking for.
The === is the strict comparaison operator, checking var type, allowing you to test this is the exact value you are looking for.
You can add a return with an other value (here -1) if the value is not found at the end of the loop.
function myIndexOf(array, search) {
for(var val in array){
if(array[val] === search){
return val;
}
}
return -1;
}
var myArray = ['a', 'b', 'c'];
console.log(myIndexOf(myArray, 'c')); //2
console.log(myIndexOf(myArray, 'f')); //-1 <- Not found
This would work:
var index = 0;
var target = 'c';
var collection = ['a', 'b', 'c', 'd'];
function findIndex(){
for(var val in collection) {
if(collection[val] === target){
return index;
}
index++;
}
}
findIndex();
You can use every as well. Iterate through the array, return true if value doesn't match value being searched so every continues, otherwise set ind and return false.
var getIndex = function(arr, match){
var ind = -1;
arr.every(function(val, index) {
if (val === match){
ind = index;
return false;
}
return true;
});
return ind;
}
getIndex([1, 2, 3], 2);

Deleting Element After Pushing

If I have an array where I am pushing certain elements to a second array- how can I delete those elements from the first array after pushing them to the second? Here is sample code:
for(var a = 0; a < arr.length; a+=1){
if(arr[a].length == 4){
other.push(arr[a]);
}
}
In other words, I know longer want elements arr[a] to be in arr if they have been pushed to other.
Just do a splice on that original array index to remove that element if you no longer require it.
for(var a = 0; a < arr.length;){
if(arr[a].length == 4){
other.push(arr[a]);
arr.splice(a, 1);
}
else {
a += 1;
}
}
This seems fine:
for(var a = 0, length=arr.length; a < length; a++){
if(arr[a].length == 4){
other.push(arr[a]);
arr.splice(a,1);
}
}
Write a function which takes an input array, and a function to determine if an element should be moved. It returns a two-element array, containing the modified input, and the new array into which elements have been extracted.
function extractIf(array, condition) {
return [
array.filter(not(condition)),
array.filter( condition)
];
}
// Specify which elements are to be extracted/moved.
function condition(elt) { return elt.length === 4; }
// Little helper function to invert a function.
function not(fn) { return function() { return !fn.apply(this, arguments); }; }
Invoke this as:
var results = extractIf(arr, condition);
arr = results[0];
other = results[1];
underscore solution
If you are willing to use underscore, you could group the input by the true/false value of the condition:
var groups = _.groupBy(arr, function(elt) { return elt.length === 4; })
Your original array with the elements removed will be in groups.false, and the other array in groups.true.

Categories

Resources