6kyu_Pair of gloves_CodeWards_i++ - javascript

Code War problem
My Solution on this problem:
function numberOfPairs(gloves) {
const glove = gloves.slice().sort();
const pairs = [];
for (let i = 0; i < glove.length - 1; i++) {
if (glove[i] == glove[i+1]) {
pairs.push(glove[i]);
}
}
return pairs.length;
}
It pass the initial test but failed the attempt/random test.
2nd test
My Second Solution, I add i++. It pass the Attempt
function numberOfPairs(gloves) {
const glove = gloves.slice().sort();
const pairs = [];
for (let i = 0; i < glove.length - 1; i++) {
if (glove[i] == glove[i+1]) {
pairs.push(glove[i]);
i++ // How?
}
}
return pairs.length;
}
Can you guys help me how does i++ fix the problem.

You can also group the colors and then count the pairs using Array.prototype.reduce.
function solution(input) {
return input.reduce(
([pairs, hash], clr) => {
hash[clr] = (hash[clr] ?? 0) + 1;
return [pairs + (hash[clr] % 2 === 0 ? 1 : 0), hash];
},
[0, {}]
)[0];
}
console.log(solution(["red", "green", "red", "blue", "blue"]));
console.log(solution(["red", "red", "red", "red", "red", "red"]));

Because if you found a pair, the next element is equal to the actual and you're advancing only one element in the array at a time. If you found a pair, the next element was the one found, then you have to advance another position in the array when you found it.
Say you have this array: [1, 2, 5, 8, 9, 2, 8]
Let's number the lines to make it easier:
1 function numberOfPairs(gloves) {
2 const glove = gloves.slice().sort();
3 const pairs = [];
4 for (let i = 0; i < glove.length - 1; i++) {
5 if (glove[i] == glove[i+1]) {
6 pairs.push(glove[i]);
7 i++ // How?
8 }
9 }
10 return pairs.length;
11 }
after line 2 you will have the following sorted array:
[1, 2, 2, 5, 8, 8, 9]
with 2 pairs in it.
Then you will start iteration of the array with i = 0 so in line 6 in the first iteraction glove[i] is 1 and glove[i+1] is 2. Different, so the for continues to i = 1.
Now the if in line 6 tests glove[i] with i = 1 making glove[i] = 2 and glove[i+1] also = 2 (first pair). So it enters the if and pushes the first number to pairs: 2.
Now if i++ isn't present in line 7, what would happen is that for would countinue to i = 2 making glove[i] = 2 and glove[i + 1] = 5. But the 2 makes part of the first pair encountered. So this is incorrect. We must skip the 2nd 2 that's what i++ is line 7 is there for. It would be even worse if the 4th element in the array was 2 again because it would inform another pair when there is only one.
After this for will continue not to i=2 but correctly to i=3 testing elements 5 and 8 for the next pair.
Hope this explains clearly enough.

Related

Why does this code work and why do we have to increment index twice?

I am learning how to code Javascript. Been learning for 6 months now. I'm working through code wars problems to help me with my logic when it comes to algorithms.
I was working on this CodeWars problem Find All Pairs
Here was my logic and code:
SET a pair count variable
SET a sorretd array varible
FOR LOOP iterate though the array
SET index = 0
WHILE index < array argument
Increment index
IF the current iteration is equal to the index + 1
ADD to the pair count varible
RETURN count variable
CODE:
function duplicates(array) {
let pairResult = 0;
let sorrtedArray = array.sort();
for (let index = 0; index < sorrtedArray.length; index++) {
if (sorrtedArray[index + 1] === sorrtedArray[index]) {
pairResult += 1;
index++
console.log(index);
}
}
return pairResult;
}
The test output I was getting with the two test cases were:
console.log(duplicates([1, 2, 5, 6, 5, 2])) ====> 2
console.log(duplicates([1, 2, 2, 20, 6, 20, 2, 6, 2])); ====> 5
I know it was counting 2s three times, at least that is what it seems. Anyways, I had to look at the solution. Below is a code that was almost identical to mine that worked.
function duplicates(array){
//Make the magic happen
const newArray = array.sort((a,b) => a-b);
if (newArray.length <= 1) return 0;
let count = 0;
for (let i = 0; i < newArray.length ; i++) {
if (newArray[i] == newArray[i+1]) {
count++;
i++;
}
}
return count;
}
My question is why are we incrementing the i++ again within the for loop when were incrementing it already when we declare the for loop?
It's there to avoid over-counting duplicates. As the requirements say:
If there are more pairs of a certain number, count each pair only once. E.g.: for [0, 0, 0, 0] the return value is 2 (= 2 pairs of 0s)
For example, given
[2, 2, 2]
you'd want to count one set of pairs.
When a pair is found
for (let i = 0; i < newArray.length ; i++) {
if (newArray[i] == newArray[i+1]) {
count++;
i++;
}
}
you've now checked off both indicies i and i + 1 - if you proceeded to compare indicies i + 1 and i + 2 on the next iteration, you'd be counting the item at i + 1 one too many.
By doing i++ inside the loop, you ensure that you skip over to the next unchecked item, without iterating over something you've already checked, to avoid double-counting.

Know the last number equal to another within an array in Javascript

const numbersArray = [5, 3, 1, 3, 4, 5, 3, 1];
var pastNumbers = [];
for (let i = 0; i < numbersArray.length; i++) {
const number = numbersArray[i];
pastNumbers.push(number);
var count = 0;
pastNumbers.forEach((v) => (v === number && count++));
console.log(`There are ${count} numbers before equal to ${number}. The last equal number is in the ${???} position.`);
}
I have an array called pastNumbers that stores the numbers that were previously traversed by the for loop of another array called numbersArray. Then with forEach I get the amount of previous numbers equal to number.
I want to know the position of the last number equal to number within the pastNumbers array.
How can I do it?
Updated with for loop
Use an object instead of array to track, it makes it a bit easier.
In this example, I'm going over each number and adding the position at the beginning (unshift) to pastNumber[number].
I'm using unshift so that the first item is the last position registred. That way I can look up with [0] instead of using [$.length-1] (but I'm still still using the length anyway so you push is fine too 🤷‍♂️)
const numbersArray = [5, 3, 1, 3, 4, 5, 3, 1];
const pastNumbers = {};
for (let i = 0; i < numbersArray.length; i++) {
const number = numbersArray[i];
if (!pastNumbers[number]) {
pastNumbers[number] = [];
}
if (pastNumbers[number].length > 0) {
console.log(
`There are ${pastNumbers[number].length} numbers before equal to ${number}. The last equal number is in the ${pastNumbers[number][0]} position.`
);
}
pastNumbers[number].unshift(i);
}

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.

Assigning every possible combination of a range of numbers to variables

Given a range of numbers (1-25) how can I create a loop so that at each iteration I get a unique set of variables.
To give an example, if I was doing it with 4 numbers my variables would be:
on loop 1:
a = 1, b = 2, c = 3, d = 4,
on loop 2:
a = 1, b = 2, c = 4, d = 3
etc.
What I am trying to do is iterate over every possible number for each position (think sudoku)
so in a 3x3 grid: a = top left position, b = top middle, etc...
so similar to sudoku I would have a condition (each row = 65: a + b + c + d + e= 65)
so what I want to do is have a loop where I can assign all the values to variables:
for (something) {
var topLeft = (determined from loop)
var nextPosition = etc.
My solution is currently like so:
var numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
var a,b,c,d,e,f,g,h,i,j,k,l,n,o,p,q,r,s,t,u,v,w,x,y;
var vars = [a,b,c,d,e,f,g,h,i,j,k,l,n,o,p,q,r,s,t,u,v,w,x,y];
var counter = 0;
var found = false;
while(found == false) {
for (var asdf = numbers, i = asdf.length; i--; ) {
var random = asdf.splice(Math.floor(Math.random() * (i + 1)), 1)[0];
vars[i] = random;
}
if (
{a+b+c+d+e = 65,
f+g+h+i+j = 65,
k+l+1+n+o = 65,
p+q+r+s+t = 65,
u+v+w+x+y = 65,
a+f+k+p+u = 65,
b+g+l+q+v = 65,
c+h+1+r+w = 65,
d+i+n+s+x = 65,
e+j+o+t+y = 65,
u+q+1+i+e = 65,
a+g+1+s+y = 65}
) {
console.log(a,b,c,d,e,f,g,h,i,j,k,l,n,o,p,q,r,s,t,u,v,w,x,y);
found = true;
}
counter++;
}
However the obvious problem with this is that it's just randomly selecting values. So it will take an incredible amount of time. I can't work out how to iterate over every possible combination (without having 25 for loops) so I can check which values will pass the condition.
Given a range of numbers (1-25) how can I create a loop so that at each iteration I get a unique set of variables.
It sounds like you are talking about the permutations of a set. You can find a bunch of different algorithms to do this. Here is a nice one from this StackOverflow answer:
function getArrayMutations(arr, perms = [], len = arr.length) {
if (len === 1) perms.push(arr.slice(0))
for (let i = 0; i < len; i++) {
getArrayMutations(arr, perms, len - 1)
len % 2 // parity dependent adjacent elements swap
? [arr[0], arr[len - 1]] = [arr[len - 1], arr[0]]
: [arr[i], arr[len - 1]] = [arr[len - 1], arr[i]]
}
return perms
}
getArrayMutations([1, 2, 3])
> [ [ 1, 2, 3 ],
[ 2, 1, 3 ],
[ 3, 1, 2 ],
[ 1, 3, 2 ],
[ 2, 3, 1 ],
[ 3, 2, 1 ] ]
Be careful though! Permutations are factorial which means they grow really fast.
P(n, k) =
This means that if you want to permute 25 numbers, you are looking at 1.551121e+25 possible combinations which is getting into the not-computable-in-your-lifetime territory.
What I am trying to do is iterate over every possible number for each position (think sudoku) so in a 3x3 grid: a = top left position, b = top middle, etc...
Two dimensional arrays (really just lists of lists) are a great way to store matrix data like this. It doesn't fundamentally change the math to change the representation from a single array, but it might be easier to think about. I'm not 100% sure if you want a 3x3 grid or a 5x5 grid but I'll assume 5x5 since you have 25 numbers in your example. You can easily reshape them like this:
function reshapeArray(array, n=5) {
let result = []
let row = 0
let col = 0
for (let i = 0; i < array.length; i++) {
if (col == 0) {
result[row] = []
}
result[row][col] = array[i]
col++
if (col == n) {
col = 0
row++
}
}
return result
}
reshapeArray([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25])
> [ [ 1, 2, 3, 4, 5 ],
[ 6, 7, 8, 9, 10 ],
[ 11, 12, 13, 14, 15 ],
[ 16, 17, 18, 19, 20 ],
[ 21, 22, 23, 24, 25 ] ]
so similar to sudoku I would have a condition (each row = 65: a + b + c + d + e= 65)
Now that you have your data in an iteratable array, you can very easily check this or any other constraint. For example:
/**
* Checks if a matrix (a 2-d array like the output from reshapeArray())
* meets our criteria.
*/
function checkMatrix(matrix) {
for (let row = 0; row < matrix.length; row++) {
let rowSum = 0
for (let col = 0; col < matrix[row].length; col++) {
rowSum += matrix[row][col]
}
// The row sum does not equal 65, so this isn't the one!
if (rowSum != 65) {
return false
}
}
// All the row sums equal 65
return true
}
If you want add extra rules (like having the columns sum to 65 as well) just modify the code to check for that. You can get the value at any point in the matrix by indexing it matrix[row][col] so matrix[0][0] is the upper-left, etc.
However the obvious problem with this is that it's just randomly selecting values. So it will take an incredible amount of time. I can't work out how to iterate over every possible combination (without having 25 for loops) so I can check which values will pass the condition.
Yes, it will. Sudoku is an NP-Hard problem. If you haven't seen complexity classes before, that's just a very mathematically formal way of saying that there's no clever solution that's going to be significantly faster than just checking every possible solution. This hypothetical problem is not exactly the same, so it might be possible, but it has a very np-ish feel to it.
Currently, your pseudocode solution would look like this:
let permutations = getPermutations() // You're going to need to change this part
// because getting all the permutations
// ahead of time will take too long.
// Just picking random numbers each time is
// not actually a terrible idea. Or, look at
// generator functions (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
for (let permutation of permutations) {
let matrix = reshapeArray(permutation)
if (checkMatrix(matrix)) {
console.log("Found it")
console.log(matrix)
break
}
}
If there is only one possible solution that matches your criteria, you will never find it this way. If there is a relatively high density of solutions, you will probably find some. If you really want to solve this problem I would recommend first looking at it from a mathematical perspective -- can you prove that it is or isn't NP? can you make some prediction about the density of solutions?
Not sure what the question really is. I would store the range in an Array:
function range(start, stop = null, upperCase = false){
let b = start, e = stop, s = 'abcdefghijklmnopqrstuvwxyz', x;
const a = [], z = s.split(''), Z = s.toUpperCase().split('');
if(typeof b === 'string'){
s = z.indexOf(b.toLowerCase());
if(e === null){
x = z.length;
}
else{
x = z.indexOf(e.toLowerCase())+1;
}
if(upperCase){
return Z.slice(s, x);
}
return z.slice(s, x);
}
else if(e === null){
e = b; b = 1;
}
for(let i=b; i<=e; i++){
a.push(i);
}
return a;
}
function permuCount(array){
let c = 1;
for(let i=0,n=1,l=array.length; i<l; i++,n++){
c *= n;
}
return c;
}
function comboCount(array){
let l = array.length;
return Math.pow(l, l);
}
console.log(range(2, 23)); console.log(range(10)); console.log(range('d'));
console.log(range('g', 'p')); console.log(range('c', 'j', true));
// here is where you'll have an issue
const testArray = range(0, 9);
console.log(permuCount(testArray));
console.log(comboCount(testArray));
As you can see there are way too many combinations. Also, you should have already see the following post: Permutations in JavaScript?

algorithm of insert and merge integer to the nested array

I couldn't find the simple and readable way to solve it.
the operations on single element array like [6] in javascript is horrible...
Question
originArray is a sorted, non-overlapping array.
and input is always a single positive integer.
if input number 4 is already inside [[2,4],[7,8]], nothing will change.
if input number 13 is new to all ranges, it will insert it to the right, sorted position, like [[2,4],[7,8],[13]]
please help me with JavaScript native function (reduce prefer) or lodash.
Expected Behavior as below examples:
Example 1
originArray [[1,3],[7,10]]
input 5
output [[1,3],[5],[7,10]]
input 6
output [[1,3],[5,10]]
6 is will connect 5 and 7, so make it [5,10].
input 13
output [[1,3],[5,10],[13]]
Example 2
originArray []
input 3
output [[3]]
input 4
output [[3,4]]
input 2
output [[2,4]]
input 7
output [[2,4],[7]]
Here is a simple solution:
function combineValues(originArray) {
var i = 0;
while (i < originArray.length-1) {
if (originArray[i+1][0] - originArray[i][originArray[i].length-1] <= 1) {
// combine intervals
var newInterval = [originArray[i][0], originArray[i+1][originArray[i+1].length-1]];
originArray.splice(i, 2);
originArray.splice(i, 0, newInterval);
} else {
i++;
}
}
}
function updateRanges(originArray, element) {
for (var i = 0; i <= originArray.length; i++) {
// if smaller than current range, insert new singleton or extend
if (i === originArray.length || element < originArray[i][0]) {
originArray.splice(i, 0, [element]);
break;
}
}
combineValues(originArray);
}
// testing
var updates = [3, 8, 7, 2, 9, 10, 1, 5, 6, 13];
var arr = [];
for (var u = 0; u < updates.length; u++) {
updateRanges(arr, updates[u]);
console.log(arr);
}
If you are concerned with the time complexity of your program, you can look into binary searching.

Categories

Resources