Why does my solution work and this other solution doesnt? - javascript

If you notice the result of the for loop that is starting at index 0 and counting up isn't taking the number 23 out fo the array.
Using JavaScript Loop through evenArray removing all values that aren't even
var evenArray = [1,2,3,6,22,98,45,23,22,12];
for (var i = evenArray.length - 1; i >= 0; i--) {
if(evenArray[i] % 2 != 0){
evenArray.splice(i, 1);
}
};
console.log(evenArray);
//output to this will be 2, 6, 22, 98, 22, 12;
var evenArray = [1,2,3,6,22,98,45,23,22,12];
for (var i = 0; i < evenArray.length; i++) {
if(evenArray[i] % 2 != 0){
evenArray.splice(i, 1);
}
};
console.log(evenArray);
//output is [2, 6, 22, 98, 23, 22, 12];

When you splice a number out of the array, all of the values after that point in the array get shifted to the left.
In the second approach, at index 6 the value is 45. You detect it as odd, so you splice. Now the 23, 22, and 12 get shifted over so that 23 is now at index 6. But since you are iterating forward, your i++ moves you up to index 7, effectively skipping the 23, which is why 23 is in the result.
When you iterate backwards, you avoid this problem because all of the numbers being shifted have already been acted on.

Dylan's answer is correct explaining why one works and the other fails.
Regardless, a simple solution that will work is:
var evenArray = [1,2,3,6,22,98,45,23,22,12];
evenArray = evenArray.filter(function(val) { return val % 2 == 0; });
console.log(evenArray);

Just adding one single print into the code:
var evenArray = [1,2,3,6,22,98,45,23,22,12];
for (var i = 0; i < evenArray.length; i++) {
console.log("index:"+i+" value:"+evenArray[i])
if(evenArray[i] % 2 != 0){
evenArray.splice(i, 1);
}
}
The output like this:
index:0 value:1
index:1 value:3
index:2 value:22
index:3 value:98
index:4 value:45
index:5 value:22
index:6 value:12
Compare to the origin array, it's cutting.
The reason is splice is cutting your array dynamically, so the index is changed after splice, value 23 origin index is 7, but after the splice it move to before index 6
[1,2,3,6,22,98,45,23,22,12];

Related

Sequence in an array

I have an algo exercise to do and I have no idea how to do it, as the requirement is to use only basic programming concepts. So - the point is to find the longest sequence of numbers in an array that are growing or not changing value.
So for the array [1,1,2,4,0,1,7,4], it would be [1,1,2,4].
It should have as small time and memory complexity as possible. Any solutions, tips? Much love and thanks in advance for any advice or feedback.
That's what I've managed to do in the last 10 minutes, but I feel like I'm doing it in the most complex way possible...
function idk(array) {
var current = 0;
var winner = 0;
var currentArray = [];
var winnerArray = [];
for (let i = 0; i <= array.length; i++) {
if (array[i + 1] >= array[i]) {
currentArray.push(array[i]);
current = currentArray.length;
} else {
currentArray.push(array[i]);
if (currentArray.length > best.length) {
// copy array and append it to the new array?
}
}
}
return winnerArray;
}
Try this javascript algorithm:
var array = [1, 8, 1, 1, 5, 7, 2, 2]
var output = []
array.forEach(function(value, index) {
if (array[index - 1] <= value && index != 0) {
output[output.length - 1].push(value)
} else {
output.push([value])
}
})
var longestArray = []
output.forEach(function(arrayCompare, index) {
if (arrayCompare.length > longestArray.length || index == 0) {
longestArray = arrayCompare
}
})
console.log(longestArray)
The first forEach loops through the elements of array. If the element is larger than or equal to the previous element, it adds it to the last array in output. If it is not, then it creates a new array, and pushes the array into output. This creates arrays with "growing" sequences.
After that, it loops through each sequence, and checks if the length of the sequence is greater than the current longest sequence, which is stored in longestArray. If it is, it changes longestArray to that. If it isn't, it does nothing.
Note that both of these loops have exceptions if the index is 0, since there is no element with index -1 (therefore such an exception had to be made).
Also, here's the same implementation in python:
array = [1, 8, 1, 1, 5, 7, 2, 2]
output = []
index = 0
while index < len(array):
value = array[index]
if (array[index-1] <= value and index !=0):
output[-1].append(value)
else:
output.append([value])
index += 1
longestArray = []
index = 0
while index < len(output):
arrayCompare = output[index]
if index==0 or len(arrayCompare) > len(longestArray):
longestArray = arrayCompare
index += 1
print(longestArray)
Just why loop over the end of the array?
Because you could take this advantage to gather the last longest sequence without having a check after the loop for having found a longest sequence.
But in parts:
Why not a temporary array? Because there is no need to use it, if you collect the values. the startignn index of a sequence is important and the actual indec to decide if the sequence is longer then the previously found one.
The loop feature two conditions, one for continuing the loop, if in sequence and another to check if the actual ended sequence is longer. And for storing the actual index.
The last loop with a check for an undefined value is false, it does not continue the loop and the nect check reveals either a new longest sequence or not.
Some other annotation:
winnerArray has to be an empty array, because of the check later for length
The sequence check take the previous element, because the loop starts with the first index and the previous element is given.
The Big O is O(n).
function idk(array) {
let winnerArray = [],
index = 0;
for (let i = 1; i < array.length + 1; i++) {
if (array[i - 1] <= array[i]) continue;
if (i - index > winnerArray.length) winnerArray = array.slice(index, i);
index = i;
}
return winnerArray;
}
console.log(...idk([1, 1, 2, 4, 0, 1, 7, 4])); // [1, 1, 2, 4]
console.log(...idk([1, 8, 1, 1, 5, 7, 2, 2])); // [1, 1, 5, 7]
console.log(...idk([1, 8, 1, 1, 5, 7, 2, 2, 2, 2, 2]));
Here is the dynamic programming algorithm in Python:
def longest_sequence(numbers):
length = len(numbers)
L = [0] * length #L stores max possible lengths at each index
L[-1] = 1 # base case
for i in range(length-2, -1, -1):
max_length = L[i]
for j in range(i, length):
if (L[j] > max_length) and (numbers[j] > numbers[i]):
max_length = L[j]
L[i] = max_length + 1
#trace back
max_length = max(L)
result = []
for k in range(max_length, 0, -1):
result.append(numbers[L.index(k)])
numbers = numbers[L.index(k):]
L = L[L.index(k):]
return result
my_numbers = [36,13,78,85,16,52,58,61,63,83,46,19,85,1,58,71,26,26,21,31]
print(longest_sequence(my_numbers))
#>>[13, 16, 52, 58, 61, 63, 83, 85]
To be optimal, you should not use intermediate lists during processing. You only need to hold indexes, sizes and the previous value:
def longSeq(A):
longStart,longSize = 0,0 # best range so far (index and size only)
start,size,prev = 0,0,None # current range (index and size)
for i,a in enumerate(A):
if i == 0 or prev <= a: # increase current range
size += 1
prev = a
if size > longSize: # track longest so far
longStart,longSize = start,size
else:
start,size,prev = i,1,a # sequence break, restart current
return A[longStart:longStart+longSize]
output:
print(longSeq([1,1,2,4,0,1,7,4])) # [1, 1, 2, 4]
print(longSeq( [1,8,1,1,5,7,2,2])) # [1, 1, 5, 7]
This will perform in O(n) time and use O(1) space.

Non-negative array subtraction in JavaScript

The answer found here is so close to what i am looking for but not quite there. What I am looking is I have an array of integers, and a value that I need to subtract from across said array. i want to zero out each index before moving on to the next. I don't want any numbers less than 0 nor do i want any decimals.. so, for subtractFromSet([4, 5, 6, 7, 8], 25) instead of returning:
[0,0,0.666666666666667,1.666666666666666,2.666666666666666]
I want:
[0,0,0,0,5]
You can loop until there is nothing left to subtract.
function subtractFromArr(arr, val){
const res = [...arr];//copy
for(let i = 0; i < res.length && val > 0; i++){
const take = Math.min(res[i], val);
res[i] -= take;
val -= take;
}
return res;
}
console.log(subtractFromArr([4, 5, 6, 7, 8], 25));
You can loop through the array "number" of times, while subtracting 1 from the current index and moving to the next index when index is equal to 0
function subtractFromSet(array, number) {
var i = 0;
do {
array[i]--;
number--;
if(array[i] == 0) i++;
} while (number != 0);
return array;
}
subtractFromSet([4, 5, 6, 7, 8], 25);

How can I store numbers in arrays?

I built a program that check if there are two common numbers in two different arrays, and then log those numbers. I was able to do that using a simple for loop that goes trough each element of the first array and check if there is an equal element in the second array. Each of the same element in the arrays are stored in a third array called "commonNumbers" which I logged at the end of the program.
const firstNumbers = [12, 45, 6, 78]
const secondNumbers = [6, 7, 12, 45]
let commonNumbers = []
for (let i = 0; i < firstNumbers.length; i++) {
for (let j = 0; j < secondNumbers.length; j++) {
if (firstNumbers[i] === secondNumbers[j]) {
commonNumbers += secondNumbers[j]
}
} }
console.log(commonNumbers)
The result for this example is the seguent:
12456
[Finished in 0.2s]
My question is about the result. I can see that the program actually worked and logged the same element in the arrays (12, 45, 6), but I can't figure out why "commonNumbers" stored the result in such a way that there are no spaces between the numbers.
I would like to clearly see each number.
For example if I call the first element of "commonNumbers" (of index 0):
commonNumbers[0] the result I will get is not going to be "12" as expected, but "1".
Same thing happen if I say: commonNumbers[2] the result is going to be "4", not "6".
Apparently "commonNumbers" array stored the element in a different way I was expecting. How can I solve this, using this "storing" method?
This is because +=, on your array, implicitly convert it to a string, as you can see in the example below, where a Number is summed to an Array.
console.log(typeof([] + 1));
Just use the comfortable .push (read more about push here) method of arrays in order to add the element:
const firstNumbers = [12, 45, 6, 78]
const secondNumbers = [6, 7, 12, 45]
let commonNumbers = []
for (let i = 0; i < firstNumbers.length; i++) {
for (let j = 0; j < secondNumbers.length; j++) {
if (firstNumbers[i] === secondNumbers[j]) {
commonNumbers.push(secondNumbers[j]);
}
} }
console.log(commonNumbers)
As a (final) side note, there are several other ways to accomplish your task, the cleverest you can probably go with is filter. You may also would take care of eventual duplicates, since if your input array has two identical numbers the commonsNumber result will contain both, which might be unintended.
The "definitive" clever solution that tries to also take care of duplicates and to loop the shorter array would be something like this:
// Inputs with duplicates, and longer array on second case.
const firstNumbers = [12, 45, 6, 78, 12, 12, 6, 45];
const secondNumbers = [6, 7, 12, 45, 45, 45, 12, 6, 99, 19, 5912, 9419, 1, 4, 8, 6, 52, 45];
// Performance: look for duplicates starting from the shortest array. Also, make a set to remove duplicate items.
const [shortestArray, longestArray] = firstNumbers.length < secondNumbers.length ? [firstNumbers, secondNumbers] : [secondNumbers, firstNumbers];
// Remove duplicates.
const dedupes = [...new Set(shortestArray)];
// Find commomn items using filter.
const commons = dedupes.filter(i => longestArray.indexOf(i) > -1);
console.log('commons is', commons);
Don't get me wrong, the solution is fine, just wanted to add "something" to the boilerplate, to take care of eventual additional scenarios.
const firstNumbers = [12, 45, 6, 78]
const secondNumbers = [6, 7, 12, 45]
let commonNumbers = []
for (let i = 0; i < firstNumbers.length; i++) {
for (let j = 0; j < secondNumbers.length; j++) {
if (firstNumbers[i] === secondNumbers[j]) {
commonNumbers.push(secondNumbers[j])
}
} }
The push method appends values to an array.
You seem to be looking for array.prototype.push (mdn). E.g.:
const firstNumbers = [12, 45, 6, 78]
const secondNumbers = [6, 7, 12, 45]
let commonNumbers = []
for (let i = 0; i < firstNumbers.length; i++)
for (let j = 0; j < secondNumbers.length; j++)
if (firstNumbers[i] === secondNumbers[j])
commonNumbers.push(secondNumbers[j]);
console.log(commonNumbers); // as an array
console.log(commonNumbers.join(', '));
why "commonNumbers" stored the result in such a way that there are no spaces between the numbers.
The + operator will try to cast its operands to compatible types. In this case, that is a string, where empty arrays [] are cast to empty strings '', and numbers 3 are cast to the corresponding string '3'. E.g. [] + 3 is the string '3'.
console.log([], typeof []);
console.log(3, typeof 3);
console.log([] + 3, typeof ([] + 3));

Every n times, skip n items and increase n by 1

This is probably an odd question since I have a solution (below), but was hoping someone could show me a more succinct or readable way to do this:
I created a loop that outputs the following array:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91]
the gaps between numbers get progressively larger:
1-0 = 1
3-1 = 2
6-3 = 3
10-6 = 4
...
91-78 = 13
etc.
I did it by creating two variables, step keeps track of the gap size and count keeps track of the current 'position' in the gap. count counts down to zero, then increases step by one.
var output = [];
var step = 0;
var count = 0;
for (var i = 0; i < 100; i++) {
if (count == 0){
step += 1;
count = step;
output.push(i);
}
count -= 1;
}
You can try the following:
var output = [];
var total = 0;
for (var i=1; i < 100; i++) {
output.push(total);
total += i;
}
The gaps between numbers simply increase by one for each step, so a for loop should be able to track this change.
You should skip useless iterations. If you want a sequence of 100 numbers, use
var output = [];
var step = 0;
for (var i = 0; i < 100; i++) {
step += i;
output.push(step);
}
If you want the general term,
aₙ = ∑ⁿᵢ₌₀ i = n*(n+1)/2
So you can also do
var output = [];
for (var i = 0; i < 100; i++) {
output.push(i * (i+1) / 2);
}
You can save the total helper variable with this solution:
var output = [0]
for (var i = 1; i < 14; i++) {
output.push(output[i - 1] + i)
}
console.log(output) // [ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91 ]
This solution takes into account that the value to add the counter value to is already present at the last position in the array.
A recursive version is also possible:
output = (function f(x) {
return x.length == 14 ? x : f(x.concat([x[x.length - 1] + x.length]))
})([0])
console.log(output); // [ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91 ]
Here is no additional counter variable is needed. I use concat because it returns an array what I need for the recursive call, where push returns the new array length. The argument for concat is an array with one element with the new value to add.
Try online

Can anyone tell me why this loop isn't working as it should?

This is for an exercise on codewars.com. The idea is to make a function that takes an array as the first parameter, then deletes each item in sequence defined by the second parameter, so if the second parameter is 3, it'll delete 3 first(counting for this one is supposed to be 1 based, not 0 based), then 6, then 9, then back around to 2, as though all the items were in a circle, then 7 (because 3 and 6 are gone), etc, then return the items in the order in which they were deleted (this pattern is referred to as a Josephus permutation).
So here's my code:
function josephus(items, k) {
var arr = [];
var l = items.length;
var a = k - 1;
for (var i = 0; i < l; i++) {
arr.push(items[a]);
items.splice(a, 1);
a += k - 1 ;
if (a >= items.length) { a = a - items.length; }
}
return arr;
}
It works sometimes. It worked right with josephus([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1), but then with josephus([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2), it worked until the last number(5, in this case), then returned null. In fact, most times, it returns null in the place of the last item. Can anyone tell me why it's doing this? If you have a codewars account, you can try it out in its context here: http://www.codewars.com/kata/5550d638a99ddb113e0000a2/train/javascript
Your index re-calculation isn't working. i.e. a = 3, items = [1] a becomes 2, items[2] is undefined. Try this code:
function j(items,k){
var arr = [];
var l = items.length;
var a = k - 1;
for(var i = 0; i<l; i++){
arr.push(items[a]);
items.splice(a, 1);
a += k - 1 ;
a = a % items.length;
}
return arr;
}
Replace
arr.push(items[a]);
with
arr.push(items[a%items.length]);

Categories

Resources