Finding the integer that appears an odd number of times - javascript

Question
Given an array of integers, find the one that appears an odd number of times.
There will always be only one integer that appears an odd number of times.
My Attempt
function findOdd(A) {
let newArr = A.sort();
let altArr = [];
let count = 0;
let firstCharacter = newArr[0];
for (let i = 0; i < newArr.length; i++) {
if (newArr[i] == firstCharacter) {
count++;
} else {
let subArr = newArr.slice(newArr[0], count);
console.log(subArr);
altArr.push(subArr);
count = 0;
firstCharacter = newArr[i];
}
}
for (let i = 0; i < altArr.length; i++) {
if (altArr[i].length % 2 != 0) {
return altArr[i][0];
}
}
}
console.log(findOdd([20, 1, -1, 2, -2, 3, 3, 5, 5, 1, 2, 4, 20, 4, -1, -2, 5]))
Problem
I used console logs which showed me that the slices are returning an empty array Which I think is why this algorithm is broken. But why does it return an empty array? And is there any other mistakes i have missed?

You are making this problem much more complicated than it actually is. A naive implementation would simply use an object to store the frequency of each element and then iterate over it at the end to find the element that appeared an odd amount of times.
function findOdd(arr) {
const freq = {};
for(const num of arr){
freq[num] = (freq[num] || 0) + 1;
}
return +Object.keys(freq).find(num => freq[num] % 2 == 1);
}
A more efficient implementation could leverage the properties of the bitwise XOR (^), namely the fact that a ^ a == 0, a ^ 0 == a, and that the operation is commutative and associative, leading to the solution of applying XOR on each element of the array to obtain the answer.
function findOdd(arr) {
return arr.reduce((a,c)=>a ^ c, 0);
}

newArr.slice(newArr[0], count);
slice takes 2 parameters and they need to be index, not an actual value. So the above code is definitely not correct.
I think you can use dictionary to simplify this algorithm. Here is my approach.
function findOdd(A) {
const appearances = {};
A.forEach(val => {
appearances[val] = (appearances[val] || 0) + 1;
});
const oddNumbers = Object.keys(appearances).filter(key => appearances[key] % 2 != 0);
return oddNumbers.length > 0 ? oddNumbers[0] : NaN;
}

After sorting, you could take a single loop and check the value against the last value (initialize without a value).
function findOdd(array) {
let count = 0;
let last;
array.sort();
for (let i = 0; i < array.length; i++) {
if (array[i] === last) {
count++;
continue;
}
if (count % 2) return last;
last = array[i];
count = 1;
}
return last;
}
console.log(findOdd([20, 1, -1, 2, -2, 3, 3, 5, 5, 1, 2, 4, 20, 4, -1, -2, 5]))
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

how to sum consecutive numbers of array until remaining only one [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last month.
Improve this question
I want to sum the numbers of array in javascript, and create others rows of this sum, until it get only one
I want to generate new rows as result of the previous sum.
it is only for training algorithms
EX: [1,2,3,4,5,6,7,8]
[3,7,11,15]
[10,26]
[36]
function retsum(x) {
for(let i = 0; i < x.length - 1; i++) {
console.log(x[i] + x[i + 1] )
}
}
retsum([2,3,5,6,10,20])
this is my code. I dont know how to generate other loop or other solution, to keep doing this sum, until remaining only one number
if you can help me, thanks
You could take a recursive approach and return a single value at the end of recursion.
function sum(numbers) {
console.log(...numbers);
if (numbers.length === 1) return numbers[0];
const result = [];
for (let i = 0; i < numbers.length; i += 2) {
result.push(numbers[i] + (numbers[i + 1] || 0));
}
return sum(result);
}
console.log(sum([1, 2, 3, 4, 5, 6, 7, 8]));
You can use while loop check if array size is above 1
function retsum(x) {
const result = [];
for (let i = 0; i < x.length - 1; i++) {
console.log(x[i] + x[i + 1] )
result.push(x[i] + x[i + 1] )
}
return result
}
let arr = [2, 3, 5, 6, 10, 20];
while (arr.length > 1) {
arr = retsum(arr)
console.log(arr)
}
Here's what I came up with using reduce. It covers empty or odd-numbered input arrays.
function addPairs(nums,left_to_right)
{
nums=nums.length>0?nums:[0]
const evenNumberOfItems=[...(nums.length%2===0?nums:left_to_right?[...nums,0]:[0,...nums])]
const pairs=evenNumberOfItems.reduce((previous,current,index,array)=>{
if(index%2===0)
previous.push([current,array[index+1]])
return previous
},[])
const addedPairs=pairs.reduce((previous,current)=>{
previous.push(current[0]+current[1])
return previous
},[]);
console.log(addedPairs)
return addedPairs.length===1?addedPairs[0]:addPairs(addedPairs);
}
console.log(addPairs([]))
console.log(addPairs([2,3,5,6,10,20,13],true))
console.log(addPairs([2,3,5,6,10,20,13],false))
If you want to sum pairs of numbers in an array, you have to decide if you are doing it left-to-right, or right-to-left. Depending on the route you go, all the intermittent arrays between the initial input and the final result will vary. Take note of the output of each snippet below.
Recursion would be the easiest way to tackle this. It all comes down to how you increment/decrement your for-loop and push/unshift the sub-array.
Left-to-right (increment)
const print = (arr) => console.log(...arr.map(JSON.stringify));
const recursiveSums = (numbers, result = [numbers]) => {
if (numbers == null || numbers.length === 0) throw new Error('array length less than 1');
if (numbers.length === 1) return result;
const subResult = [];
for (let i = 0; i < numbers.length; i += 2) {
subResult.push(numbers[i] + (numbers[i+1] ?? 0));
};
result.push(subResult);
return recursiveSums(subResult, result);
};
print(recursiveSums([1, 2, 3, 4, 5, 6, 7, 8])); // even
print(recursiveSums([5, 3, 8, 4, 1])); // odd
try { recursiveSums([]) } // error!
catch (e) { console.log(e.message); }
.as-console-wrapper { top: 0; max-height: 100% !important; }
Output
[1,2,3,4,5,6,7,8] [3,7,11,15] [10,26] [36]
[5,3,8,4,1] [8,12,1] [20,1] [21]
array length less than 1
Right-to-left (decrement)
const print = (arr) => console.log(...arr.map(JSON.stringify));
const recursiveSums = (numbers, result = [numbers]) => {
if (numbers == null || numbers.length === 0) throw new Error('array length less than 1');
if (numbers.length === 1) return result;
const subResult = [];
for (let i = numbers.length - 1; i >= 0; i -= 2) {
subResult.unshift((numbers[i-1] ?? 0) + numbers[i]);
};
result.push(subResult);
return recursiveSums(subResult, result);
};
print(recursiveSums([1, 2, 3, 4, 5, 6, 7, 8])); // even
print(recursiveSums([5, 3, 8, 4, 1])); // odd
try { recursiveSums([]) } // error!
catch (e) { console.log(e.message); }
.as-console-wrapper { top: 0; max-height: 100% !important; }
Output
[1,2,3,4,5,6,7,8] [3,7,11,15] [10,26] [36]
[5,3,8,4,1] [5,11,5] [5,16] [21]
array length less than 1
Note: You really don't have to decrement in the right-to-left example. You could just subtract the index from the length of the array and push rather than unshift.
for (let i = 0; i < numbers.length; i += 2) {
subResult.push((numbers[numbers.length - i - 2] ?? 0) + numbers[numbers.length - i - 1]);
};

Find lowest positive integer that does not appear in array

I am trying to solve a leetcode type problem that is a practice problem that came with an upcoming code test I need to do for a job and I am having trouble with it. Can anyone help me understand whats going wrong?
I am essentially looking for the brute force option as I dont know algos/DS.
PROBLEM:
Write a function:
function solution(A);
that, given an array A of N integers, returns the smallest positive integer (greater than 0) that does not occur in A.
For example, given A = [1, 3, 6, 4, 1, 2], the function should return 5.
Given A = [1, 2, 3], the function should return 4.
Given A = [−1, −3], the function should return 1.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000];
each element of array A is an integer within the range [−1,000,000..1,000,000].
HERE IS MY SOLUTION:
function solution(A) {
let newArray = A.sort(function(a, b){return a-b})
let lowestNumber = 1
for(i=0; i < newArray.length; i++) {
if(lowestNumber > newArray[0]) {
return lowestNumber
}
if(lowestNumber == newArray[i]) {
lowestNumber = lowestNumber + 1
}
if(i = newArray.length - 1) {
return lowestNumber
}
}
}
The below snippet isnt working like I expect it to. lowestNumber isnt being increased and also the loop is exiting here I believe.
if(lowestNumber == newArray[i]) {
lowestNumber = lowestNumber + 1
Thanks for your help!
You can do this in O(N) using a Map():
First set every number in the array.
Then starting from 1 look for and return the missing number in the sequence.
function solution(arr) {
const seen = new Map();
for (let i = 0; i < arr.length; i++) {
seen.set(arr[i]);
}
for (let i = 1; i <= arr.length + 1; i++) {
if (!seen.has(i)) return i;
}
return 1;
}
console.log(solution([1, 3, 6, 4, 1, 2])); //-> 5
console.log(solution([1, 2, 3])); //-> 4
console.log(solution([-1, -3])); //-> 1
I think your > should be <, and the = in if(i = newArray.length - 1) should be ===.
And lowestNumber > newArray[0] will always be true if the array contains a negative number, so 1 will be returned.
Your effort seems careless, so you are going to have to up your game for the interview.
const integers = [5, -345, 562456, 95345, 4, 232, 1, 2, 3, 7, -457];
function solution(A) {
let newArray = A.sort((a, b) => a - b);
let lowestNumber = 1;
for (let i = 0; i < newArray.length; i++) {
const n = newArray[i];
if (n > 0) {
if (lowestNumber < n) {
return lowestNumber;
} else {
lowestNumber = n + 1;
}
}
}
return lowestNumber;
}
console.log(solution(integers));
The fastest solution
function solution(A) {
// write your code in JavaScript (Node.js 8.9.4)
if (!A) return 1;
A.sort();
if (A[A.length - 1] < 1) return 1;
const setA = new Set(A);
let length = setA.size;
for (let i = 1; i <= length; i++) {
if (!setA.has(i)) {
return i;
}
}
return length + 1;
}
I have worked same problem for nowadays, and regardless the original answer, here is my version of finding least positive number which is missing the in the array.
function findLeastPositive(array) {
const numbersSet = new Set(array);
let leastPositiveNumber = 1;
while(numbersSet.has(leastPositiveNumber)) {
leastPositiveNumber++;
}
return leastPositiveNumber;
}
let result = findLeastPositive([1,2,3,4,5,6,7,8,9,0]);
console.log(result);
result = findLeastPositive([10,11,12,13,14,15,16,17,18,19]);
console.log(result);
There are sure similar answers floating on the internet but using given array length disturbing me of which I can't explain properly why we have to create second loop starts with 1 (known the least positive number) and to N.
Using hash table (I am using Set here) for lookup table is fine idea, as in it effect to overall performance O(N) (probably initialize the Set with array) and O(1) for checking if the value in the Set or not.
Then we need to set second loop for obvious reason that checking the the smallest positive number existence, starting from 1..N range. This is the part bugged me, so I decided to go for while loop. It's obvious rather why there's a for..loop starts from 1..N on which N is the length of the array.
Here is 100% code
function solution(A) {
/**
* create new array of positive numbers in given array,
* if there sis no specific number in given array, in result array
* that index will be undefine
*/
const c = A.reduce((arr, cur) => {
if(cur > 0) arr[cur] = 1;
return arr;
} , [1] )
/**
* return first undefined index
*/
for(let i = 0; i < c.length; i++)
if(!c[i]) return i;
// otherwise return the maximum index in array
return c.length;
}
function solution(arr) {
for (let i = 1; i <= arr.length + 1; i++) {
if (!arr.includes(i)) return i;
}
return 1;
}
console.log(solution([1, 3, 6, 4, 1, 2])); //-> 5
console.log(solution([1, 2, 3])); //-> 4
console.log(solution([-1, -3])); //-> 1

Find int that appears an odd number of times in an array

I am working on this task where I need to find a number that happens to appear an odd number of times in an array.
I believe I've almost done it, but if some number appears more than once in a row (like 1 in [1,1,3,1,1]), it will always return that number, no matter if it appears an odd number of times or not.
function findOdd(A) {
var a;
var count = 0;
for (var i = 0; i < A.length; i++) {
a = A[i];
for (var l = i + 1; l < A.length; l++) {
if (a == A[l]) {
count++;
}
}
if (!(count % 2)) {
break;
} else {
count = 0;
}
}
return a;
}
console.log(findOdd([ 1, 1, 2, -2, 5, 2, 4, 4, -1, -2, 5 ]));
I've tried to play with adding 1 to count if [i] = [i+1], but it didn't work.
I'd expect output of findOdd([1, 1, 2, -2, 5, 2, 4, 4, -1, -2, 5]) to be -1, but it is 1. The function always returns first number that happens to be equal to next element of an array.
There's no need to reset count or use a break.
function findOdd(A) {
for (var i = 0; i < A.length; i++){
var count = 0;
for (var l = 0; l < A.length; l++) {
if (A[i] === A[l]) count++;
}
if (count % 2 !== 0) return A[i];
}
}
An important thing to note is that the inner loop is not starting at i+1, its starting at the 0. When A[i] matches A[l], we increment count. A number that appears an odd number of times will result in count becoming odd as well and we can return that number.
The following works but I wonder how the performance compares to simply doing for loops. The complexity seems to be the same.
function findOdd(a) {
let m = {};
a.forEach(e => (m[e] in m) ? m[e] += 1 : m[e] = 1);
for (k in m) {
if (m[k] % 2 != 0) return k;
}
}
console.log(findOdd([1, 1, 3, 1, 1]));
console.log(findOdd([1, 1, 2, -2, 5, 2, 4, 4, -1, -2, 5]));
You could count all values first and then get the value with an odd count.
function getOddCount(array) {
var value,
count = {},
k;
for (value of array) count[value] = (count[value] || 0) + 1;
for (k in count) if (count[k] % 2) return +k;
}
console.log(getOddCount([1, 1, 3, 1, 1]));
console.log(getOddCount([1, 1, 2, -2, 5, 2, 4, 4, -1, -2, 5]));
A naive implementation would simply use an object to store the frequency of each element and then iterate over it at the end to find the element that appeared an odd amount of times.
function findOdd(arr) {
const freq = {};
for(const num of arr){
freq[num] = (freq[num] || 0) + 1;
}
return +Object.keys(freq).find(num => freq[num] % 2 == 1);
}
A more efficient implementation could leverage the properties of the bitwise XOR (^), namely the fact that a ^ a == 0 and that the operation is commutative and associative, leading to the solution of applying XOR on each element of the array to obtain the answer.
function findOdd(arr) {
return arr.reduce((a,c)=>a ^ c, 0);
}

Why my Move Zeroes function is wrong answer in leetcode?

I was doing Move Zeroes in leetcode.
I write a function to solve but leetcode said it's a wrong answer.
Could someone see what is wrong in my code?
Requirement:the original array must be mutated
Input:[0,1,0,3,12]
Output:[1,3,12,0,0]
Input:[2,1]
Output:[1,2]
Here is my JS:
var moveZeroes = function(nums) {
var ZeroArray=[]
for(let i=0;i<nums.length;i++){
if(nums[i]===0){
ZeroArray.push(nums[i])
nums.splice(i,1);
}
}
nums.sort((a,b)=>(a-b))
for(let j=0;j<ZeroArray.length;j++){
nums.push(ZeroArray[j])
}
return nums;
};
console.log(moveZeroes([0,1,0,3,12])); //Should return [ 1,3,12,0,0]
console.log(moveZeroes([2,1]));//Should return [1,2]
Your
nums.shift(nums[i]);
will remove (and discard) whatever exists at the 0th index in nums at the time. It would probably be easier to push to a different array if the num is not 0, then combine the arrays at the end (no sorting):
var moveZeroes = function(nums) {
var ZeroArray = []
var nonZeroArray = [];
for (let i = 0; i < nums.length; i++) {
if (nums[i] === 0) {
ZeroArray.push(nums[i])
} else {
nonZeroArray.push(nums[i]);
}
}
return [...nonZeroArray, ...ZeroArray];
};
console.log(moveZeroes([0, 1, 0, 3, 12])) //Should return [ 1,3,12,0,0]
Or, if you do want to .sort, .sort only:
var moveZeroes = function(nums) {
nums.sort((a, b) => (a === 0) - (b === 0));
return nums;
};
console.log(moveZeroes([0, 1, 0, 3, 12])) //Should return [ 1,3,12,0,0]
Do check === 0 - otherwise, your
.sort((a,b)=>(a-b))
will put negative numbers after the 0s, when you want 0s to come at the end regardless.
If you also need to preserve the original order of non-zeros, iterate over the array, splice out 0s while keeping track of how many you remove, then push them at the end:
var moveZeroes = function(nums) {
let count = 0;
for (let i = nums.length - 1; i >= 0; i--) {
if (nums[i] === 0) {
nums.splice(i, 1);
count++;
}
}
nums.push(...new Array(count).fill(0));
return nums;
};
console.log(moveZeroes([0, 1, 0, 3, 12])) //Should return [ 1,3,12,0,0]
Array.shift() removes the first element from an array, you most likely want to use Array.splice(i, 1)
Use filter to filter all the non zero elements and then append remaining array size with zeros.
let arr = [0,1,0,3,12];
let filtered = arr.filter(item => item !== 0);
let result = [...filtered, ...(new Array(arr.length - filtered.length).fill(0))]
console.log(result);
Using splice remove zero and count the spiced values. In a loop push the zeroes in the array equal to the count
var a=[0,1,0,3,12];
var count=0;
a.forEach(e=>{
if(e==0){
a.splice(a.indexOf(e),1)
count++
}})
for(var i=0;i<count;i++)
a.push(0)
console.log(a)
You could take a single loop approach with an additonal variable for the next index for swapping the values.
This approach works in situ, as the specs requires.
/**
* #param {number[]} nums
* #return {void} Do not return anything, modify nums in-place instead.
*/
var moveZeroes = function(nums) {
var i, j = 0;
for (i = 0; i < nums.length; i++) {
console.log(...nums); // look!
if (nums[i] !== 0) { // check
[nums[j], nums[i]] = [nums[i], nums[j]]; // swap items
j++; // increment target
}
}
},
array = [0, 1, 0, 3, 12];
moveZeroes(array)
console.log(...array); // [1, 3, 12, 0, 0]
here is code to remove zeros in c++
void pushZerosToEnd(int arr[], int n)
{
int count = 0;
for (int i = 0; i < n; i++)
if (arr[i] != 0)
arr[count++] = arr[i];
while (count < n)
arr[count++] = 0;
}
int main()
{
int arr[] = {1, 9, 8, 4, 0, 0, 2, 7, 0, 6, 0, 9};
int n = sizeof(arr) / sizeof(arr[0]);
pushZerosToEnd(arr, n);
cout << "Array after pushing all zeros to end of array :n";
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
return 0;
}
var a = [1, 2, 0, 0, 3, 0, 3, 0, 2, 0, 0, 0, 5, 0];
function moveZero(b) {
for (var i = b.length - 1; i >= 0; i--) {
if (b[i] === 0) {
b.splice(i, 1);
b.push(0);
}
}
return b;
}
console.log(moveZero(a));

Find longest occurrence of same number in array

Using JavaScript, I'm trying to find a way to find the longest occurrence of the same number (in this case, 1) in an array.
For instance, here's a sample array:
[2,5,3,1,1,1,3,7,9,6,4,1,1,1,1,1,4,7,2,3,1,1,4,3]
I'd like to write a function that would return "5", since the number 1 occurs 5 times in a row. (It also occurs 3 and 2 times in a row, but I'm after the longest occurrence).
So far, I have written:
function streak(arr) {
var i,
temp,
streak,
length = arr.length;
for(i=0; i<length; i++) {
if (arr[i] === 1) {
streak += 1;
} else {
temp = streak;
break;
}
}
}
I know I need some way of knowing where I left off if I find an occurrence, but I'm feeling kind of stuck.
Any pointers?
I've modified your function slightly. You need to store the highest streak as a separate variable from the current streak, and overwrite that where necessary in your loop - finally returning that variable at the end of your function.
function streak(arr) {
var i,
temp,
streak,
length = arr.length,
highestStreak = 0;
for(i = 0; i < length; i++) {
// check the value of the current entry against the last
if(temp != '' && temp == arr[i]) {
// it's a match
streak++;
} else {
// it's not a match, start streak from 1
streak = 1;
}
// set current letter for next time
temp = arr[i];
// set the master streak var
if(streak > highestStreak) {
highestStreak = streak;
}
}
return highestStreak;
}
var array = [2,5,3,1,1,1,3,7,9,6,4,1,1,1,1,1,4,7,2,3,1,1,4,3];
console.log(streak(array)); // 5
And if you want to also track what the value of the highest streak was, define another variable at the start of your function, save the value of it when you save the highest streak, and return it as an array:
// set the master streak var
if(streak > highestStreak) {
highestStreakValue = temp;
highestStreak = streak;
}
}
return [highestStreak, highestStreakValue];
var array = [2,5,3,1,1,1,3,7,9,6,4,'a','a','a','a','a',4,7,2,3,1,1,4,3];
console.log(streak(array)); // [5, "a"]
Demo returning both
An alternative approach. I'm converting the array to a string. The regular expression has a backrefence, which ensures that only sequences of the same character are matched. Also when exec is used with the g flag, repeated executions will continue from the end of last match, and not from the beginning.
var arr = [2,5,3,1,1,1,3,7,9,6,4,1,1,1,1,1,4,7,2,3,1,1,4,3];
var str = arr.join('');
var regex = /(.)\1*/g;
var match;
var largest = '';
while (match = regex.exec(str)) {
largest = match[0].length > largest.length ? match[0] : largest;
}
console.log(largest.length);
Your problems:
You don't store current streak
You don't specify when streak is more then older streak
Use this:
function streak(arr) {
var i,
temp,
streak = 1,
maxStreak = 0,
prevNumber,
length = arr.length;
for(i=1; i<length; i++) {
prevNumber = arr[i-1];
if (arr[i] == prevNumber) {
streak += 1;
} else {
if(streak > maxStreak) {
maxStreak = streak;
streak = 1;
}
}
}
return maxStreak;
}
Demo
You will need another two arrays here.
Store the distinct numbers from your source array using a loop
Make a second set of array which is equal to the length of the first set of array which has the distinct numbers.
Make a loop equal to the length of the first set of array and then push the values to the second set of array according to its index.
Make a loop again using the second set of array and there you will find the most occurence using the index of the second array
Finally, get from the first set of array the number using the index you got from step 4.
I did not make the code for you to try it yourself first since you are asking only for some pointers
Alternative: use regexp and converting the array to a string.
var arr = [2,5,3,1,1,1,3,7,9,6,4,1,1,1,1,1,4,7,2,3,1,1,4,3];
var str = arr.join('').match(/1+/g);
console.log(process ? process.sort().pop() : "No ocurrences");
You could take Array#reduce and return the start index of the actual same item sequence. Then check and update the counter if the item is not equal.
var array = [2, 5, 3, 1, 1, 1, 3, 7, 9, 6, 4, 1, 1, 1, 1, 1, 4, 7, 2, 3, 1, 1, 4, 3],
maxCount = 0,
maxValues;
array.reduce(function (j, a, i, aa) {
if (aa[j] === a) {
return j;
}
if (i - j === maxCount){
maxValues.push(aa[j]);
}
if (i - j > maxCount) {
maxCount = i - j;
maxValues = [aa[j]];
}
return i;
}, -1);
console.log(maxCount);
console.log(maxValues);
My proposal:
function getLongestRow(inputArray) {
// Initialize dummy variables
var start = inputArray[0], curRowLen = 0, maxRowLen = 0, maxRowEle = 0;
// Run through the array
for(var i = 0;i < inputArray.length;i++) {
// If current Element does not belong to current row
if(inputArray[i] != start) {
// If current row is longer than previous rows, save as new longest row
if(curRowLen > maxRowLen) {
maxRowLen = curRowLen;
maxRowEle = start;
curRowLen = 1;
}
// Start new row
start = inputArray[i];
} else {
// Current element does belongt to current row, increase length
curRowLen++;
}
}
// Check whether last row was longer than previous rows
if(curRowLen > maxRowLen) {
maxRowLen = curRowLen;
maxRowEle = start;
}
// Return longest row & element longest row consits of
console.log('The longest row in your array consists of '+maxRowLen+' elements of '+maxRowEle+'.');
}
JsFiddle: http://jsfiddle.net/hdwp5/
Here's a way to do it:
var values = function(obj) {
var res = [];
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
res.push(obj[i]);
}
}
return res;
};
var countStreak = function(xs) {
var res = xs.reduce(function(acc, x, i) {
if (x === xs[i+1]) {
acc[x] = acc[x]+1 || 2;
} else {
acc[x] = acc[x]-1 || 0;
}
return acc;
},{})
return Math.max.apply(0, values(res));
};
var ns = [2,5,3,1,1,1,3,7,9,6,4,1,1,1,1,1,4,7,2,3,1,1,4,3]
countStreak(ns) //=> 5
You can use fewer iterations by looking ahead at all matches from a given index,
and jumping ahead to the next non-matching item's index.
You can also quit when there are less items left than the maximum you have found.
function maxRepeats(arr){
var L= arr.length, i= 0,
max= 1, count= 0;
while(L-i > max){
while(arr[i+count]=== arr[i])++count;
if(count > max) max= count;
i+= count;
count= 0;
}
return max;
}
var A= [2, 5, 3, 1, 1, 1, 3, 7, 9, 6, 4, 1,
1, 1, 1, 1, 4, 7, 2, 3, 1, 1, 4, 3];
maxRepeats(A); returns 5
Finding multiple items that repeat the max number of times is not so easy,
since you have to find the max number before you can list them.
If you really only need the max number, ignore this:
function mostRepeats(arr, maximum){
var i= 0, max= maximum || 1,
L= arr.length-max,
count= 0, index= [];
while(i<L){
while(arr[i+count]=== arr[i])++count;
if(count=== maximum) index.push(arr[i]+' starting at #'+i);
else if(count > max) max= count;
i+= count;
count= 0;
}
if(max===1) return 'No repeats';
return maximum? max+' repeats of: '+index.join(', '): mostRepeats(arr, max);
}
var A= [2, 5, 3, 1, 1, 1, 3, 7, 9, 6, 4, 1, 1, 1,
1, 1, 4, 7, 2, 3, 3, 3, 3, 3, 1, 1, 4, 3];
mostRepeats(A);returns:
5 repeats of: 1 starting at #11, 3 starting at #19
Unfortunately I can't comment yet due to lack of reputation so I will post this as an answer. For my task Robbie Averill's solution was perfect, but it contains a little bug. I had array that consisted of 2 values - 0 & 1.5, but above-mentioned code was counting only "1.5" values although I had "0" repeating in a higher streak. Problem was that value wasn't doing strict comparison here:
if(temp != '' && temp == arr[i]) {
and the fix was simple: if(temp !== '' && temp == arr[i]) {
I've updated Robbie's jsfiddler with this fix: http://jsfiddle.net/d5X2k/5/
Unfortunatly, a question has been marked as duplicate, but it was not the same as this one. So I must put my answer here, sorry…
let tab = [0,0,0,1,1,1,0,0,0,0,1,0,1,1,1,1,1]
, arr = []
, n = 0
, res = null ;
for(let i of tab)
{
if ( i ) { ++ n }
else if ( n ) { arr.push(n) ; n = 0 }
}
arr.push(n) ;
res = Math.max(...arr);
console.log("Streak with 1 is ", Math.max(...arr));
It's a better solution than with reduce, slower, as you can see:
let tab = [0,0,0,1,1,1,0,0,0,0,1,0,1,1,1,1,1];
let arr = [];
let n = 0;
let res = null;
let loop = 0;
let start = new Date().getTime();
while (loop < 1000000){
++ loop;
arr = [];
for(let i of tab)
{
if ( i ) { ++ n }
else if ( n ) { arr.push(n) ; n = 0 }
}
arr.push(n);
res = Math.max(...arr);
}
let end = new Date().getTime();
console.log("laps old fashion = ", end - start);
loop = 0;
let streaks = null;
start = new Date().getTime();
while (loop < 1000000){
++ loop;
streaks = tab.reduce((res, n) =>
(n ? res[res.length-1]++ : res.push(0), res)
, [0]);
res = Math.max(...streaks);
}
end = new Date().getTime();
console.log("laps reduce = ", end - start);
console.log("Streak with 1 is ", Math.max(...arr));
Input array:
const seq = [
0, 0, 0,
1, 1, 1,
1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
];
Shortest solutions:
console.log(Math.max(...Array.from(seq.join("").matchAll(/(.)\1+/g), m=>m[0].length)))
Alternative with regexp (spoiler: it's ~25%, slower than solution with reduce(). See "Modern approach with reduce()" below):
const longestSeq = (seq) => {
let max = 0;
seq.join("").replace(/(.)\1+/g, m=> max = Math.max(max, m.length));
return max;
};
Straightforward, old-school style, human readable and fastest solution:
let longestSeq = () => {
let maxCount = 0,
curCount = 0,
curItem, prevItem,
l = seq.length+2, // +1+1 to finish last sequence and compare 'undefined' with previous
i = 0;
for (; i < l; ++i) {
curItem = seq[i];
if (curItem === prevItem) ++curCount;
else {
if (curCount > maxCount) maxCount = curCount;
curCount = 1;
prevItem = curItem;
}
}
return maxCount;
}
Modern approach with reduce() (just very little slower than old-school code above):
const longestSeq = (seq) => seq
.reduce(
({count, max}, item) => item === 0
? { count: ++count, max: Math.max(count, max) }
: { count: 0, max: max },
{ count: 0, max: 0} )
.max;
Performance test, Reduce() vs old-school for(): https://jsbench.me/ifkgsin56z/1

Categories

Resources