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]
Related
First off am a beginner practicing my JavaScript. My solution to this problem will be posted. I think its worth mentioning this took almost two days of pondering to solve
The Problem:
I am required to write an algorithm that will return the mode(s) from the given input array. For example:
mode([4, 5, 6, 6, 6, 7, 7, 9, 10]) ➞ [6]
mode([4, 5, 5, 6, 7, 8, 8, 9, 9]) ➞ [5, 8, 9]
mode([1, 2, 2, 3, 6, 6, 7, 9]) ➞ [2, 6]
Solution:
function mode(nums) {
let array = [...nums]
array = array.sort((a, b) => a - b) //sorts the array from lowest value
// function to figure out the unique numbers and return as an array
function uniqueNums(array) {
let uniques = []
for (let i = 0; i < array.length; i++) {
if (!uniques.includes(array[i])) {
uniques.push(array[i])
}
}
return uniques
}
//function to return the mode of every unique number
function counter(array) {
let modes = []
for (let i = 0; i < array.length; i++) {
let count = 1, // keeps track of occurrence's of a number
track = 1 //variable enables the while loop keep checking
while (array[i] === array[i + track]) {
count++
track++
}
modes.push(count)
i += count - 1
}
return modes
}
//function to return the highest mode(s)
function highestMode(uniques, modes) {
let highest = [],
max = 0 //tracks our highest number in the array
//loops to find highest mode
for (let i = 0; i < modes.length; i++) {
if (max < modes[i]) {
max = modes[i]
}
}
//loops to push position of modes equal to the highest mode
for (let i = 0; i < modes.length; i++) {
if (max === modes[i]) {
highest.push(i)
}
}
//uses the position of highest modes to swap them with their
//actual values
let result = highest.map(a => a = uniques[a])
return result
}
return highestMode(uniqueNums(array), counter(array))
}
console.log(mode([4, 4, 4, 6, 8, 9, 10, 10]))
If you're looking at this as a learning exercise, here's another implementation of the same algorithm from CertainPerformance, but written quite differently.
const mode = (
ns,
counts = ns .reduce ((m, n) => m .set (n, (m .get (n) || 0) + 1), new Map ()),
max = Math .max (... counts .values())
) =>
[...counts] .flatMap (([n, c]) => c == max ? [n] : [])
console .log (mode ([4, 5, 6, 6, 6, 7, 7, 9, 10])) //=> [6]
console .log (mode ([4, 5, 5, 6, 7, 8, 8, 9, 9])) //=> [5, 8, 9]
console .log (mode ([1, 2, 2, 3, 6, 6, 7, 9])) //=> [2, 6]
If it's not clear, my counts matches to grouped and my max to maxCount.
The big difference from the answer by CertainPerformance is that this is written with pure expressions rather than statements. This is a style that I try to follow as much as I can do so practically.
It uses default parameters as a poor-man's substitute for let bindings available in other languages. It's not a perfect substitute and there is at least one potential problem with it, but it can be quite useful. Doing this let me define some helper variables that you can't assign in a function body without statements. There are alternatives, but I find this simplest when I can get away with it.
The reduce call is essentially the same as the other answer. So is the max definition.
But I take advantage of the fact that the default iterator of a Map is the list of entries, to turn the map into [[4, 1], [5, 1], [6, 3] ...] just by using the spread operator (...), without a need to call .entries().
Finally, I replace a call to filter and then to map with a single call to flatMap. This feels more elegant.
I'm not trying to suggest that this code is better than the one from CertainPerformance. It's quite similar in feel, although different in arrangement. But it is a different approach to the problem, and it might have something to offer because of that.
I'd count up the number of occurrences of each element into an Map. Then use Math.max to find the largest value(s) in the map, and then take the keys which are equal to that largest value:
const mode = arr => {
const grouped = new Map();
for (const item of arr) {
grouped.set(item, (grouped.get(item) || 0) + 1);
}
const maxCount = Math.max(...grouped.values());
return [...grouped.entries()]
.filter(([, count]) => count === maxCount)
.map(([key]) => key);
};
console.log(mode([4, 5, 6, 6, 6, 7, 7, 9, 10])) // ➞ [6]
console.log(mode([4, 5, 5, 6, 7, 8, 8, 9, 9])) // ➞ [5, 8, 9]
console.log(mode([1, 2, 2, 3, 6, 6, 7, 9])) // ➞ [2, 6]
A more simpler and optimized approach with single iteration
https://jsfiddle.net/dv7f9nxr/
function mode(items) {
var result = [];
var count = {};
var highest= 0;
items.map((item) => {
var itemCount = (count[item] || 0) + 1;
count[item] = itemCount;
if(itemCount > highest) {
highest = itemCount;
//reset
result = [item];
} else if (itemCount === highest){
result.push(item);
}
})
return result;
}
console.log(mode([2, 3, 9, 6, 9]))
console.log(mode([2, 3,2, 9, 6, 9]))
console.log(mode([2, 3,2, 9, 6, 9,2]))
console.log(mode([2, 3, 9, 6, 9,6,2]))
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
Considering, I have an array like this [..., n-2, n-1, n, n+1, n+2, ...]. I would like to sort it in this way [n, n+1, n-1, n+2, n-2,...] with n equals to the middle of my array.
For example:
Input:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Output:
[5, 6, 4, 7, 3, 8, 2, 9, 1, 0]
let arrayNotSorted = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let positionMiddleArray = Math.trunc(arrayNotSorted.length / 2);
let arraySorted = [arrayNotSorted[positionMiddleArray]];
for(let i=1; i <= positionMiddleArray; i++){
if(arrayNotSorted[positionMiddleArray + i] !== undefined){
arraySorted.push(arrayNotSorted[positionMiddleArray + i]);
}
if(arrayNotSorted[positionMiddleArray - i] !== undefined){
arraySorted.push(arrayNotSorted[positionMiddleArray - i]);
}
}
console.log('Not_Sorted', arrayNotSorted);
console.log('Sorted', arraySorted);
What I have done works properly, but I would like to know if there is a better way or a more efficient way to do so ?
You could take a pivot value 5 and sort by the absolute delta of the value and the pivot values an sort descending for same deltas.
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
pivot = 5;
array.sort((a, b) => Math.abs(a - pivot) - Math.abs(b - pivot) || b - a);
console.log(...array); // 5 6 4 7 3 8 2 9 1 0
You can do that in following steps:
Create an empty array for result.
Start the loop. Initialize i to the half of the length.
Loop backwards means decrease i by 1 each loop.
push() the element at current index to the result array first and the other corresponding value to the array.
function sortFromMid(arr){
let res = [];
for(let i = Math.ceil(arr.length/2);i>=0;i--){
res.push(arr[i]);
res.push(arr[arr.length - i + 1])
}
return res.filter(x => x !== undefined);
}
console.log(sortFromMid([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
[5, 6, 4, 7, 3, 8, 2, 9, 1, 0]
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const output = move(numbers, 3, -5);
console.log(output);
function move(array, index, offset) {
const output = [...array];
const element = output.splice(index, 1)[0];
output.splice(index + offset, 0, element)
return output;
}
The first line is an array of numbers.
At the second line, when calling the move function, we pass three arguments.
First, is the array itself called numbers.
Secondly, the index of the number we are trying to move (in the example, we have index 3 so we are passing the number 4).
Finally, we have the offset set to -5. The negative sign means we are moving the number to the left. The 5 means 5 positions.
But as you can see, we only have 3 positions to the left of the number 4 before reaching the beginning of the array. In this case, we have to go to the end of the array and count backwards. So, we are looking for a function which will turn the original array to [1, 2, 3, 5, 6, 7, 8, 4, 9].
As you can see, number 4 has shifted 3 positions to the left to reach the beginning of the array, then, 2 further positions from the end of the array.
A further example to clarify.
Let's say we write:
const output = move(numbers, 1, -4);
In this example, we want the number 2 from the array (index 1) to move 4 positions to the left. So, we should get [1, 3, 4, 5, 6, 7, 2, 8, 9].
You need to cover the edge cases when the updated index is less than 0 OR greater than the array length. You can try following
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function move(array, index, offset) {
const output = [...array];
const element = output.splice(index, 1)[0];
let updatedIndex = index + offset;
if(updatedIndex < 0) updatedIndex++;
else if (updatedIndex >= array.length) updatedIndex -= array.length;
output.splice(updatedIndex, 0, element);
return output;
}
console.log(move(numbers, 3, -5));
You could do this using while loop and iterating for the Math.abs() of the position you want to move to and then move in direction depending if parameter is positive or negative.
function move(arr, i, p) {
let left = p < 0,
counter = Math.abs(p),
newPos = i;
while (--counter > -1) {
newPos = (left ? (newPos - 1) : (newPos + 1));
if (newPos == -1) newPos = arr.length - 1;
if (newPos == arr.length) newPos = 0;
if (counter == 0) arr.splice(newPos, 0, arr.splice(i, 1)[0])
}
return arr;
}
console.log(move([1, 2, 3, 4, 5, 6, 7, 8, 9], 3, -5));
console.log(move([1, 2, 3, 4, 5, 6, 7, 8, 9], 5, 5));
console.log(move([1, 2, 3, 4, 5, 6, 7, 8, 9], 1, -25));
i wants to separate an array with two groups (odd and even) sequentially. but when i try this:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < arr.length; i++) {
if (arr[i]%2 == 0) {
arr.push(arr.splice(i, 1)[0]);
}
}
console.log(arr);
console.log(arr);
// [1, 3, 5, 7, 9, 4, 8, 6, 2]
why 4,8,6,2 instead of 2,4,6,8?
Because you move every found even value to the end of the array:
0: 1 2 3 4 5 6 7 8 9
^-------------v
1: 1 3 4 5 6 7 8 9 2 3 is not checked, because of the incremented index after splicing
^-----------v
2: 1 3 5 6 7 8 9 2 4 5 is not checked
^---------v
3: 1 3 5 7 8 9 2 4 6 7 is not checked
^-------v
4: 1 3 5 7 9 2 4 6 8 9 is not checked
^-----v
5: 1 3 5 7 9 4 6 8 2
^---v
6: 1 3 5 7 9 4 8 2 6
^-v
7: 1 3 5 7 9 4 8 6 2
|
8: 1 3 5 7 9 4 8 6 2
But, you do not check the value after the found even value, like the value 3 in line zero, because it is never checked and stays at this place, as well as other actually uneven values. You could try the whole again with all even at the beginning of the array, like
var array = [2, 4, 6, 8, 1, 3, 5, 7, 9];
for (var i = 0; i < array.length; i++) {
if (array[i] % 2 === 0) {
array.push(array.splice(i, 1)[0]);
console.log(i + ': ' + array.join(' '));
}
}
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Solution
You could use a length for checking and reduce the length for every found even value. In this case, the index stays at the value, because at the index is now the next element for checking.
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9],
i = 0,
l = array.length;
while (i < l) {
if (array[i] % 2 === 0) {
array.push(array.splice(i, 1)[0]);
l--;
continue;
}
i++;
}
console.log(array);
Or just sort.
By moving odd values to top and then sort by value.
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
array.sort((a, b) => b % 2 - a % 2 || a - b);
console.log(array);
The way you do it is so complicated. You can simply achieve that with array.prototype.filter and array.prototype.concat:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr = arr.filter(e => e%2).concat(arr.filter(e => e%2 === 0));
console.log(arr);
why 4,8,6,2 instead of 2,4,6,8?
Basically when half of the iterations are done, it is re-processing the pushed items.
You need to change the for-loop as
for (var i = 0; i < arr.length/2; i++) {
if (arr[i]%2 == 0) {
arr.push(arr.splice(i, 1)[0]);
}
}
Demo
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < arr.length / 2; i++) {
if (arr[i] % 2 == 0) {
arr.push(arr.splice(i, 1)[0]);
}
}
console.log(arr);
Edit
And if the order of items is not always sorted, then you apart from sorting it first, you can reduce the length variable.
var arr = [1, 2, 4, 3, 5, 6, 7, 8, 9];
var length = arr.length;
for (var i = 0; i < length; i++) {
console.log(i,arr[i]);
if (arr[i] % 2 == 0) {
arr.push(arr.splice(i, 1)[0]);
console.log("result ", arr)
length--;
i--;
}
}
console.log(arr)
why 4,8,6,2 instead of 2,4,6,8?
it is because you are modifying the same Array which you are looping through.
to understand better, lets loop through your code
after
1st Loop: array: [1, 2, 3, 4, 5, 6, 7, 8, 9];
2nd loop: array: [1, 3, 4, 5, 6, 7, 8, 9, 2];
3rd loop: array: [1, 3, 5, 6, 7, 8, 9, 2, 4];
4th loop: array: [1, 3, 5, 7, 8, 9, 2, 4, 6];
5th loop: array: [1, 3, 5, 7, 9, 2, 4, 6, 8];
6th loop: array: [1, 3, 5, 7, 9, 4, 6, 8, 2];
7th loop: array: [1, 3, 5, 7, 9, 4, 8, 2, 6];
8th loop: array: [1, 3, 5, 7, 9, 4, 8, 6, 2];
9th loop: array: [1, 3, 5, 7, 9, 4, 8, 6, 2];
to separate out odd/even in an array,
we will have to filter out odd and even separately and push evenArray in the end of oddArray(filtered Array)
var input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
input = input.sort((a, b)=> a-b); //to sort array if not sorted already
var evenArr = [];
input = input.filter(item => {
if (item % 2 != 0)
return true
else {
evenArr.push(item);
return false;
}
})
Array.prototype.push.apply(input, evenArr);
console.log(input);
I believe this can be covered by lodash _.partition:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var [odds, evens] = _.partition(arr, function(n) {
return n % 2;
});
console.log(_.concat(odds, evens));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Or even shorter:
_.groupBy(arr, function(n) {return (n % 2) ? 'odd' : 'even';})