How to iterate over an array multiple times without repeating summed elements - javascript

I am trying to solve this problem but I don't know why I can't pass all test cases. I need some help and explanation, how can I count some array (in this example: variable s) multiple times and not repeat the same elements that I already summed.
Problem description:
Lily has a chocolate bar that she wants to share it with Ron for his
birthday. Each of the squares has an integer on it. She decides to
share a contiguous segment of the bar selected such that the length of
the segment matches Ron's birth month and the sum of the integers on
the squares is equal to his birth day. You must determine how many
ways she can divide the chocolate.
Consider the chocolate bar as an array of squares, s=[2,2,1,3,2].
She wants to find segments summing to Ron's birth day, d=4 with a
length equalling his birth month, m=2. In this case, there are two
segments meeting her criteria: [2,2] and [1,3].
Function Description
Complete the birthday function in the editor below. It should return
an integer denoting the number of ways Lily can divide the chocolate
bar.
birthday has the following parameter(s):
s: an array of integers, the numbers on each of the squares of
chocolate,
d: an integer, Ron's birth day, m: an integer, Ron's birth month
My code:
function birthday(s, d, m) {
let bars = 0;
if (m !== 1) {
s.reduce((acc, val) => (acc+val) === d ? ++bars : bars)
} else {
bars = 1;
}
return bars;
}
Some cases:
s = [2, 5, 1, 3, 4, 4, 3, 5, 1, 1, 2, 1, 4, 1, 3, 3, 4, 2, 1]
d = 18
m = 7
s = [4, 5, 4, 5, 1, 2, 1, 4, 3, 2, 4, 4, 3, 5, 2, 2, 5, 4, 3, 2, 3,
5, 2, 1, 5, 2, 3, 1, 2, 3, 3, 1, 2, 5]
d = 18
m = 6
s = [4, 5, 4, 2, 4, 5, 2, 3, 2, 1, 1, 5, 4]
d = 15
m = 4
My code works with this:
s = [1, 2, 1, 3, 2]
d = 3
m = 2
This can be found on HackerRank > Practice > Algorithms > Implementation

You just have to slice the array with the sliced length of m, and then compare that to d
As slice doc:
The slice() method returns a shallow copy of a portion of an array into a new array object selected from start to end (end not included) where start and end represent the index of items in that array. The original array will not be modified.
For example:
s = [1, 2, 1, 3, 2]
m = 2
d = 3
// We loop through s with index stop at s.length - m + 1 for slice to be in correct range
// Slices:
i=0: [1, 2] -> sum=3 -> res=0+1=1
i=1: [2, 1] -> sum=3 -> res=1+1=2
i=2: [1, 3] -> sum=4 -> do nothing
i=4: [3, 2] -> sum=5 -> do nothing
Below is a worked solution
function birthday(s, d, m) {
let res = 0
const sum = (arr) => arr.reduce((acc, el) => acc + el, 0)
for (let i = 0; i < s.length - m + 1; i++) {
if (sum(s.slice(i, i + m)) === d) {
res++
}
}
return res
}

Whenever you are looping over an array to get the summation or do a mathematical equation on it and you have to remove that specific element that you already calculated, You can use one of these built in function to remove an element from an array using a specific index.
Array.prototype.slice()
&& Array.prototype.splice()

Here's an easy to understand way with nested loops:
function birthday(s, d, m) {
var matches = 0; // Total matches found
// Look at chunks starting a position 0. Last chunk can't be m spots past end of array, so last chunk starts at 1 + s.length - m:
for ( let i=0; i < 1 + s.length - m; i++ ) {
var sum = 0; // What this chunk sums to
// Sum up the values of this chunk:
for ( let j=0; j < m; j++ ) {
sum += s[i+j];
}
if ( sum === d ) { // Does this chunk sum to d?
matches++; // Yes!
}
}
return matches;
}

Related

Find the longest sub array of consecutive numbers with a while loop

I want to write a function with a while-statement that determines the length of the largest consecutive subarray in an array of positive integers. (There is at least one consecutive array.) For instance:
Input: [6, 7, 8, 6, 12, 1, 2, 3, 4] --> [1,2,3,4]
Output: 4
Input: [5, 6, 1, 8, 9, 7] --> [1,8,9]
Output: 3
Normally I would try to use for-loops and the array.push method later on, however, to get more practice I wanted to use a while-loop and another 'array-lengthening' method, not sure how it's called, see below.
My try:
function longestSub (input) {
let i=0;
let idx=0;
let counterArr=[1]; //init. to 1 because input [4,5,3] equals sub-length 2
while(i<input.length) {
if (input[i]+1 > input[i]) {
counterArr[0+idx] += 1
}
else {
i=input.indexOf(input[i]); //should start loop at this i-value again
idx +=1;
counterArr[0+idx] = 1; //should init new array index
}
i++
}
return Math.max(...counterArr)
}
My idea was that the else-statement would reset the if-statement when it fails and start again from the position it failed at with updated variables. It would also initialize another array index with value 1 that gets subsequently updated afterwards with the if-statement.
Finally I have a counterArr like [1,2,3] where 3 stands for the largest consecutive subarray. Thanks everyone reading this or helping a beginner like me to get a deeper understanding of Javascript.
Here is a simple solution using while loop:
let arr =[6, 7, 8, 6, 12, 1, 2, 3, 4]
let endIndx = 0, maxLength = 0, indx = 1,tempMax = 0;
while (indx < arr.length) {
if (arr[indx] > arr[indx - 1])
tempMax++;
else {
if (maxLength <= tempMax) {
maxLength = tempMax+1
endIndx = indx
tempMax=0;
}
}
++indx
}
if (maxLength < tempMax) {
maxLength = tempMax
endIndx = indx
}
console.log("Sub array of consecutive numbers: ", arr.slice(endIndx-maxLength,endIndx))
console.log("Output :",maxLength)
You could take an approach which just counts the length and checks with the max found length if the continuous items.
function longestSub(input) {
let i = 1, // omit first element and use later element before this index
max = 0,
tempLength = 1; // initialize with one
if (!input.length) return 0;
while (i < input.length) {
if (input[i - 1] < input[i]) {
tempLength++;
} else {
if (max < tempLength) max = tempLength;
tempLength = 1;
}
i++;
}
if (max < tempLength) max = tempLength;
return max;
}
console.log(longestSub([])); // 0
console.log(longestSub([6, 7, 8, 6, 12])); // 3
console.log(longestSub([5, 6, 1, 2, 8, 9, 7])); // 4
console.log(longestSub([6, 7, 8, 6, 12, 1, 2, 3, 4, 5])); // 5
Unless this really is a learning exercise, I'd rather focus on the approach than on the implementation.
Create a function that slices an array of numbers into arrays of consecutive numbers:
The first two conditions deal with the simplest cases:
If input is empty, output is empty [] -> []
If input is exactly one element, the output is known already [42] -> [[42]]
Then comes the "meat" of it. The output is an array of array. Let's start by creating the first sub array with the first element of the initial array. Let's use [6, 7, 8, 6, 12, 1 ,2 ,3, 4, 5] as the input.
Start with [[6]] then iterate over [7, 8, 6, 12, 1 ,2 ,3, 4, 5]. Here are the result at each iteration:
7 > 6 true -> [[6,7]]
8 > 7 true -> [[6,7,8]]
6 > 8 false -> [[6],[6,7,8]]
12 > 6 true -> [[6,12],[6,7,8]]
1 > 12 false -> [[1],[6,12],[6,7,8]]
2 > 1 true -> [[1,2],[6,12],[6,7,8]]
3 > 2 true -> [[1,2,3],[6,12],[6,7,8]]
4 > 3 true -> [[1,2,3,4],[6,12],[6,7,8]]
5 > 4 true -> [[1,2,3,4,5],[6,12],[6,7,8]]
const slices =
xs =>
xs.length === 0 ? []
: xs.length === 1 ? [[xs[0]]]
: xs.slice(1).reduce
( ([h, ...t], x) =>
x >= h[h.length - 1]
? [h.concat(x), ...t]
: [[x], h, ...t]
, [[xs[0]]]
);
slices([6, 7, 8, 6, 12, 1 ,2 ,3, 4, 5]);
//=> [ [1, 2, 3, 4, 5]
//=> , [6, 12]
//=> , [6, 7, 8]
//=> ]
Then you create a function that takes an array of slices and return the biggest one:
const max_slices =
xs =>
xs.reduce
( (a, b) =>
a.length > b.length
? a
: b
);
max_slices(slices([6, 7, 8, 6, 12, 1 ,2 ,3, 4, 5]));
//=> [1, 2, 3, 4, 5]

JavaScript - reverse an array in a specific way

I'm doing an algorithm course and here is the instructor's answer about how to reverse an array without using reverse js method:
function solution(arr) {
for(var i=0; i < arr.length/2; i++) {
var tempVar = arr[i]
arr[i] = arr[arr.length - 1 - i]
arr[arr.length - 1 - i] = tempVar
}
return arr
}
I did understand everything, EXCEPT this detail:
arr.length/2
In this line below:
for(var i=0; i < arr.length/2; i++) {
what does it mean? What its purpose?
To reverse a string, you have to swap characters of first half of the string with the last half.
let str = 'abcde';
You have to swap a with e, b with d.
ab is the first half of the string. So simply run loop over the first half of the string and swap ith character with arr.length - 1 - ith character as below
var tempVar = arr[i]
arr[i] = arr[arr.length - 1 - i]
arr[arr.length - 1 - i] = tempVar
Algorithm start with first and last element and swap them. Next it take the second element from the begin and from the end and swap them. And etc it swap all the elements with same distance from center.
So algorithm go to the center of array from both sides. And it need only half of length of array from both side to proceed. So that statement arr.length/2 actually the expression which is a half of length.
Which is used as limit of the loop.
The algorithm swaps two elements that are on equal distance from both ends of the array. The number of operations needed is number_of_operations = number_of_elements / number_elements_operated_on and since it's doing two elements at once, that's number_of_elements / 2. And hence the reason to use arr.length / 2 as the limit of the for loop. Here is a representation of what happens.
Given an array [1, 2, 3, 4, 5, 6] then array.length is 6 and the following operations are performed:
//loop i = 0, 0 < 3 == true, execute
[1, 2, 3, 4, 5, 6] -> [6, 2, 3, 4, 5, 1]
^--------------^ ^--------------^
//loop i = 1, 1 < 3 == true, execute
[6, 2, 3, 4, 5, 1] -> [6, 5, 3, 4, 2, 1]
^--------^ ^--------^
//loop i = 2, 2 < 3 == true, execute
[6, 5, 3, 4, 2, 1] -> [6, 5, 4, 3, 2, 1]
^--^ ^--^
//i = 3, 3 < 3 == false, loop stops
This works perfectly fine with odd number of elements, since there is going to just be one element in middle when you get to it.
Given an array [1, 2, 3, 4, 5] then array.length is 5 and the following operations are performed:
//loop i = 0, 0 < 2.5 == true, execute
[1, 2, 3, 4, 5] -> [5, 2, 3, 4, 1]
^-----------^ ^-----------^
//loop i = 1, 1 < 2.5 == true, execute
[5, 2, 3, 4, 1] -> [5, 4, 3, 2, 1]
^-----^ ^-----^
//loop i = 2, 2 < 2.5 == true, execute
[5, 2, 3, 4, 1] -> [5, 4, 3, 2, 1]
^ ^
//i = 3, 3 < 2.5 == false, loop stops

I am looking for an algorithm for dividing N numbers into K groups and for each group to have S players

I am looking for an algorithm for dividing N numbers into K groups and for each group to have S players.
split(array, k, s);
var array = [5, 5, 5, 3, 3, 2, 1, 1, 1]; // sum : 26
var k = 3;// number of groups;
var s = 3;// number of players;
// 26/3 = 8.66 => sum in each group
// result :
//group 1 : { 5, 3, 1 } sum: 9
//group 2 : { 5, 3, 1 } sum: 9,
//group 3 : { 5, 2, 1 } sum: 8,
I think a simple greedy approach is optimal. Just grab next highest from the array and put it in the group that's the smallest.
const array = [5, 5, 5, 3, 3, 2, 1, 1, 1];
let groups = [[], [], []];
array.forEach(val => {
const counts = groups.map(group => group.reduce((acc, val) => acc + val, 0));
groups[counts.indexOf(Math.min(...counts))].push(val);
});
console.log(groups);
EDIT:
Oof, didn't read well enough. Simplest approach might be to even player count after: Just keep move the smallest value from the largest group (by size) to the smallest until groups are equal size.
You'll have to convince yourself if that's optimal, I'm not sure offhand.

Given an array and positive integer d, left shift the array by d efficiently

This is the HackerRank problem description:
A left rotation operation on an array of size n shifts each of the array's elements d unit to the left. For example, if left rotations are performed on array [1,2,3,4,5], then the array would become [3,4,5,1,2].
Here is my function :
function leftRotation(arr, d) {
let newArr = [];
while (d > 0) {
let first = arr.shift();
newArr.push(first);
d--;
}
return [...arr,...newArr];
}
console.log(leftRotation([1,2,3,4,5], 2))
but it doesn't pass large test cases. For example, for n=73000 and d=60000.
Thanks in advance for any idea .
I'm not entirely sure the performance of this method, but it can be done in a single line.
function leftRotation(arr, d) {
return [ ...arr.slice(d), ...arr.slice(0, d) ];
}
console.log(leftRotation([1, 2, 3, 4, 5], 2));
Don't rotate N number of times. Shift N % (length of array) times, because Let's say you have an array of 5 items and you are asked to shift it 5 times then you essentially do not have to shift even once.
Start : [1, 2, 3, 4, 5]
1: [2, 3, 4, 5, 1]
2: [3, 4, 5, 1, 2]
3: [4, 5, 1, 2, 3]
4: [5, 1, 2, 3, 4]
5: [1, 2, 3, 4, 5]
EDIT:
You could use similar logic to optimize the code instead of actually shifting elements in the array. For example: In case of N = 73000, D = 60000, you could splice the array by 73000 % 60000 and then just append the returned spliced array to the existing array and return it.
For an array arr a method shift() can have a time complexity of O(arr.length).
If d is larger than n then you still perform shift() d times.
In total, your time complexity can rise to O(d * arr.length) that is definitely too long.
However, this problem can be solved in O(arr.length) time and space. If you know d then you can shift each item by d positions to the left easily. For instance, arr.length = 5 and d = 2
position: 0 1 2 3 4
arr: 4 3 5 1 6
position in arr: 2 3 4 0 1
shifted_arr: 5 1 6 4 3
So actually, each item at position i in the shifted array corresponds to an item at position (i + d) % arr.length in the original array arr. Hence, the code can look as follows:
function leftShift(arr, d) {
let newArr = [];
let size = arr.length;
for (var i = 0; i < size; i++) {
newArr.push(arr[(i + d) % size]);
}
return newArr;
}
console.log(leftShift([4, 3, 5, 1, 6], 2))

Jumble numbers in an array such that no two adjacent numbers are same using JavaScript

The idea it to basically not have repeated values in the array with similar values.
An example input array:
input = [1,2,2,2,2,3,4,5,6,7,8,9]
Expected output to be something like this:
desiredOutput = [1,2,3,2,4,2,5,2,6,2,7,8,9]
I have tried putting this in a for loop where it checks with the next item and if it is same, swaps the values. The problem is when I have continuous similar values.
This proposal features
count of elements and store it in an appropriate object,
check whether spread is possible (e.g. not here [1, 1, 1, 1, 3, 3]),
round robin with the elements, so
maximum distance between the same elements.
How does it work?
As example I take this array: [1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9]
Build an object with the count of the elements, store it with the element as key.
length = {
"1": 1, "2": 4, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1
}
Select the property with the largest value: length[2] = 4
Make a new array with the length of the previous value and fill it with empty arrays.
output = [[], [], [], [], []]
Check if a spreaded array is possible. If not, return.
Set k to the key of the biggest value of a property.
k = '2'
If truthy, proceed. Otherwise go to 11.
Set l to the value of length[k].
l = 4
Iterate over l and push k to the end of the array with the index of i % outputLength. Increase i.
Delete property k.
Proceed with 5.
Return the flat output array.
output first then continued
array 0: 2 1 6
array 1: 2 3 7
array 2: 2 4 8
array 3: 2 5 9
return: 2 1 6 2 3 7 2 4 8 2 5 9
distance | | | | is equal
function spread(input) {
function findMaxKey() {
var max = 0, key;
Object.keys(length).forEach(function (k) {
if (length[k] > max) {
max = length[k];
key = k;
}
});
return key;
}
var length = input.reduce(function (r, a) {
r[a] = (r[a] || 0) + 1;
return r;
}, {}),
i = 0, k = findMaxKey(), l,
outputLength = length[k],
output = Array.apply(Array, { length: outputLength }).map(function () { return []; });
if (input.length - outputLength < outputLength - 1 ) {
return; // no spread possible
}
while (k = findMaxKey()) {
l = length[k];
while (l--) {
output[i % outputLength].push(k);
i++;
}
delete length[k];
}
return output.reduce(function (r, a) { return r.concat(a) }, []);
}
console.log(spread([1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9]));
console.log(spread([1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2]));
console.log(spread([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]));
console.log(spread([1, 1, 1, 1, 3, 3]));
console.log(spread([1, 1, 3]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
maybe that could help you:
for(var i = 1; i < input.length; i++) {
if(input[i-1] == input[i]) {
var j = i;
while(j < input.length && input[j] == input[i]) {
j++;
}
var el = input[j];
input[j] = input[i];
input[i] = el;
}
}
Greedy Approach Using Max Heap
The idea is to greedily put the highest frequency numbers first.
Construct a max heap where every node is a tuple that stores the number & it's frequency.
Then extract the head of the max heap (the highest frequency node) and add it's value to the resultant array.
If there's a previous element then add it back to the heap.
Decrement the frequency of the extracted node and store it in prev, so that it can be added back after one iteration.
Finally return the solution if it exists otherwise return the string "Not Possible".
function solution(arr) {
const maxHeap = Array.from(
arr.reduce((m, i) => m.set(i, (m.get(i) ?? 0) + 1), new Map())
).sort(([, a], [, b]) => b - a);
const res = [];
let prev = null;
while (maxHeap.length) {
const maxNode = maxHeap.shift();
res.push(maxNode[0]);
maxNode[1] -= 1;
if (prev) {
maxHeap.push(prev);
maxHeap.sort(([, a], [, b]) => b - a);
prev = null;
}
if (maxNode[1] > 0) {
prev = maxNode;
}
}
return res.length < arr.length ? "Not Possible" : res;
}
console.log(JSON.stringify(solution([1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9])));
console.log(JSON.stringify(solution([1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2])));
console.log(JSON.stringify(solution([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3])));
console.log(JSON.stringify(solution([1, 1, 1, 1, 3, 3])));
console.log(JSON.stringify(solution([1, 1, 3])));
Note: I've not implemented a Max Heap (because it's tedious), I've simulated it with Array.prototype.sort.

Categories