Javascript function. What is missing? - javascript

Write a program to find count of the most frequent item of an array. Assume that input is array of integers.
Example:
Input array: [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3]
Ouptut: 5
Most frequent number in example array is -1. It occurs 5 times in input array.
Here is my code:
function mostFrequentItemCount(collection) {
var copy = collection.slice(0);
for (var i = 0; i < collection.length; i++) {
var output = 0;
for (var x = 0; x < copy.length; x++) {
if (collection[i] == copy[x]) {
output++;
}
}
}
return output;
}
It seems to be just counting the reoccurrence of the first number in the array not the one that occurs the most. I can't figure out how to make it count the most occurring one.

If i didn't miss anything, and if you really want to find the count of the most frequent item of an array, i guess one approach would be this one:
function existsInCollection(item, collection) {
for(var i = 0; i < collection.length; i++) {
if(collection[i] === item) {
return true;
}
}
return false;
}
function mostFrequentItemCount(collection) {
var most_frequent_count = 0;
var item_count = 0;
var already_checked = [];
for(var i = 0; i < collection.length; i++) {
// if the item was already checked, passes to the next
if(existsInCollection(collection[i], already_checked)) {
continue;
} else {
// if it doesn't, adds to the already_checked list
already_checked.push(collection[i]);
}
for(var j = 0; j < collection.length; j++)
if(collection[j] === collection[i])
item_count++;
if(item_count > most_frequent_count)
most_frequent_count = item_count;
item_count = 0;
}
return most_frequent_count;
}
var items = [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3];
alert(mostFrequentItemCount(items));
What happens here is:
On each item ('i' loop), it will run another loop ('j') through all items, and count how many are equal to the [i] item. After this second loop, it will be verified if that item count is greater than the most_frequent_count that we already have, and if it is, updates it.
Since we always use the same variable 'item_count' to check each number count, after the verification of each number we reset it to 0.
This may not be the best answer, but it was what occurred me at the moment.
EDIT:
I added a function to check if an item already exists in a list, to avoid the loop from check the same item again.

The problem is that you override the output variable each loop iteration, so after the for loop ends your output variable holds occurrences of the last element of input array.
You should use variables like var best_element = collection[0] and var best_element_count = -1 (initialized like this). After each inner loop you check if algo found any better solution (best_element_count < output) and update best_element.
Edit: following #Alnitak comment you should also reset the output variable after each inner loop iteration.

First you will need to construct a collection (or object) that contains the element and the count of occurances. Second you will need to iterate the result to find the key that has the highest value.
JSFiddle
function mostFrequentItemCount(collection) {
var output = {};
for (var i = 0; i < collection.length; i++) {
var item = collection[i];
if (!(item in output))
output[item] = 0;
output[item]++;
}
var result = [0, 5e-324];
for (var item in output) {
if (output[item] > result[1]) {
result[0] = parseFloat(item);
result[1] = output[item];
}
}
return result;
}
var input = [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3];
var result = mostFrequentItemCount(input);
console.log(result);
The snippet above simply creates a new object (output) which contains a property for each of the unique elements in the array. The result is something like.
2:2
3:4
4:1
9:1
-1:5
So now we have an object with the property for the number and the value for the occurances. Next we then interate through each of the properties in the output for(var item in output) and determine which value is the greatest.
Now this returns an array with the value at index 0 being the number and the value at index 1 being the count of the element.

Check this solution.
var store = [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3];
var frequency = {}; // array of frequency.
var max = 0; // holds the max frequency.
var result; // holds the max frequency element.
for(var v in store) {
frequency[store[v]]=(frequency[store[v]] || 0)+1; // increment frequency.
if(frequency[store[v]] > max) { // is this frequency > max so far ?
max = frequency[store[v]]; // update max.
result = store[v]; // update result.
}
}
alert(max);

So this update to your method will return an object with each key and the count for that key in the array. How you format an output to say what key has what count is up to you.
Edit: Updated to include the complete solution to the problem.
function mostFrequentItemCount(collection) {
var copy = collection.slice(0);
var results = {};
for (var i = 0; i < collection.length; i++) {
var count = 0;
for (var x = 0; x < copy.length; x++) {
if (collection[i] == copy[x]) {
count++;
}
}
results[collection[i]] = count;
}
return results;
}
var inputArray = [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3];
var occurances = mostFrequentItemCount(inputArray);
var keyWithHighestOccurance = Object.keys(occurances).reduce(function(a, b){ return occurances[a] > occurances[b] ? a : b });
var highestOccurance = occurances[keyWithHighestOccurance];
console.log("Most frequent number in example array is " + keyWithHighestOccurance + ". It occurs " + highestOccurance + " times in the input array.");

Related

Delete only one of the duplicated elements in ArrayList

I have a few arrays, which are filled with different values every new time. So the array sometimes contains more than one of the same value. So it could look like this: [0, 0, 0, 2, 3, 0]. I want to be able to remove one instance of a target value. For example, if the target were 0, the array might then look like this: [0, 0, 2, 3, 0].
At the moment I am working with this code:
var new_list = grades.filter(e => e !== grade);
Note: grades is the ArrayList I get from the database. But this line is going to remove all 0. But I only want to remove one of the 0.
EDIT:
Tried something like this:
let grades = doc.data()[grade_type] || [];
var elementIndex = grades.indexOf(grade);
grades.splice(elementIndex);
But does not work. Example ArrayList [0, 0, 0] . Output [].
~filip
After you detect which element has duplicates you can use splice
grades.splice(elementIndex,1);
You could delete the second appearance of the same value and take a flag for keeping all other values.
var array = [0, 0, 0, 2, 3, 0, 3, 3],
f = {};
array = array.filter(v => f.all || (f.all = !(f[v] = (f[v] || 0) + 1 !== 2)));
console.log(array);
Check next approach, here we loop over the array until we found the first duplicate of the element you want to remove, when we found it, we remove it using splice().
const grades = [0, 0, 1, 0, 1, 2, 3, 2, 3, 4];
const rmOneDupFrom = (arr, n) =>
{
let nCounter = 0;
for (let i = 0; i < arr.length; i++)
{
nCounter = arr[i] === n ? nCounter + 1 : nCounter;
if (nCounter > 1)
{
arr.splice(i, 1);
return;
}
}
}
console.log("Original => ", JSON.stringify(grades));
// Remove one duplicate of element 3.
rmOneDupFrom(grades, 3);
console.log("one 3 removed => ", JSON.stringify(grades));
// Remove one duplicate of element 0.
rmOneDupFrom(grades, 0);
console.log("one 0 removed => ", JSON.stringify(grades));
// Try to remove one duplicate of element 4.
rmOneDupFrom(grades, 4);
console.log("none removed (4 hasn't duplicates) => ", JSON.stringify(grades));
You should note that this approach mutates the original array, in case you don't want that, you can make a copy of the original array before passing it to rmOneDupFrom() with slice(), example:
let grades = [0, 0, 1, 0, 1, 2, 3, 2, 3, 4];
const rmOneDupFrom = (arr, n) =>
{
let nCounter = 0;
for (let i = 0; i < arr.length; i++)
{
nCounter = arr[i] === n ? nCounter + 1 : nCounter;
if (nCounter > 1)
{
arr.splice(i, 1);
return;
}
}
}
let gradesCopy = grades.slice(0);
rmOneDupFrom(gradesCopy, 3);
console.log("Original => ", JSON.stringify(grades));
console.log("Copy => ", JSON.stringify(gradesCopy));
UPDATE:
The following will remove the second instance of a duplicate (the earliest we can detect that a duplicate, in fact, exists):
function removeOneTargetDuplicate(list, target) {
let result = [];
let count = 0;
for (let i = 0; i < list.length; i++) {
if (list[i] === target) {
count++;
if (count === 2) {
continue;
}
}
result.push(list[i]);
}
return result;
}
The following solution will remove the first instance of a duplicate, albeit with a bit more complexity but still with linear time complexity:
function removeOneTargetDuplicate(list, target) {
let result = [];
let duplicate = false;
for (let i = 0; i < list.length; i++) {
// duplicate will only be true if we've removed the first duplicate
if (list[i] === target && !duplicate) {
// if we've found the target, check the rest of the array for a duplicate
for (let j = i + 1; j < list.length; j++) {
if (list[j] === target) {
duplicate = true;
}
}
// if that duplicate was found, skip to the next iteration of the for loop and don't add to the result, effectively removing the first instance of the target
if (duplicate) {
continue;
}
}
result.push(list[i]);
}
return result;
}

Given an array of integers return positives, whose equivalent negatives present in it

I have implemented solution in javascript using two loops, below is the code
function getNums(arr){
var res = [];
var found = {};
var i, j;
var arrLen = arr.length;
for(i=0; i<arrLen; i++){
if(!found.hasOwnProperty(arr[i])){
for(j=0; j<arrLen; j++){
if(arr[i]+arr[j] === 0){
var num = arr[i];
if(num > 0){
res.push(num);
found[num] = 1;
}
}
}
}
}
return res;
}
console.log(getNums[-1, -2, 0, -4, 1, 4, 6]); // Output: [1, 4]
Whose time complexity is O(n2). Can someone suggest better solution / refined above to have less complexity?
You can just add the array to a Set and filter for inclusion in the set. Determining if something is in a set is constant time:
let arr = [-1, 2, 3, 1 , 3, -3, 4, -6]
let s = new Set(arr)
// all positive numbers with corresponding negatives in the set
let filtered = arr.filter(item => item > 0 && s.has(-1 * item))
console.log(filtered)
An alternative is to sort the array and then walk two pointers up the array as making matches along the way. The result will be sorted, however, which may not be the same order as the original array:
let arr = [-2, -3, 2, 5, 3, 1, -6, 2, -5]
arr.sort()
// get startig indexes
let i = 0, j = arr.findIndex(n => n > 0)
let res = []
if (j > -1) { // only if there are positive numbers in the array
while(arr[i] < 0 && j < arr.length){
if (-1 * arr[i] === arr[j]){
res.push(arr[j++])
} else if(-1 * arr[i] > arr[j]){
j++
} else if(-1 * arr[i] < arr[j]){
i++
}
}
}
console.log(res)
You could take a single loop approach by counting the values.
function getNums(array) {
var count = Object.create(null),
result = [];
array.forEach(v => {
if (count[-v]) {
result.push(Math.abs(v));
count[-v]--;
return;
}
count[v] = (count[v] || 0) + 1;
});
return result;
}
console.log(getNums([1, 2, -3, -4, 2, 3, 4, 4, -4]));
Before the downvotes... This answer is not the shortest javascript code, but the algorithm - I think it is what the original question was about.
One way to get rid of nested loops is to use more memory to store intermediate structures. In your case, you want to store not just the "found" flag, but negative, positive values as well, so that at every iteration you can set the found flag. Then you also use the "found" flag to prevent adding the results 2nd time.
var f = function(arr) {
let hash = {};
let res = [];
for (var i = 0; i < arr.length; i++) {
// put value into the hash map for future use
hash[arr[i]] = arr[i];
var absVal = Math.abs(arr[i]);
// if value is not 0 AND if it has not been found yet (x+value hash) AND if both negative and positive values are present
if( arr[i] !== 0 && !hash["x"+absVal] && (hash[arr[i]] + hash[-arr[i]] === 0)){
// then set the found hash to true
hash["x"+absVal] = true;
// and push to the resut
res.push(absVal);
}
}
// return the result
return res;
}
Another solution is to use filter and includes prototype functions which are well optimized.
const getNums = (arr) => arr.filter((num, index) => num > 0 && !arr.includes(num, index + 1) && arr.includes(-num));

Return numbers which appear only once (JavaScript)

Say I have the array [1,2,3,5,2,1,4]. How do I get make JS return [3,4,5]?
I've looked at other questions here but they're all about delete the copies of a number which appears more than once, not both the original and the copies.
Thanks!
Use Array#filter method twice.
var data = [1, 2, 3, 5, 2, 1, 4];
// iterate over elements and filter
var res = data.filter(function(v) {
// get the count of the current element in array
// and filter based on the count
return data.filter(function(v1) {
// compare with current element
return v1 == v;
// check length
}).length == 1;
});
console.log(res);
Or another way using Array#indexOf and Array#lastIndexOf methods.
var data = [1, 2, 3, 5, 2, 1, 4];
// iterate over the array element and filter out
var res = data.filter(function(v) {
// filter out only elements where both last
// index and first index are the same.
return data.indexOf(v) == data.lastIndexOf(v);
});
console.log(res);
You can also use .slice().sort()
var x = [1,2,3,5,2,1,4];
var y = x.slice().sort(); // the value of Y is sorted value X
var newArr = []; // define new Array
for(var i = 0; i<y.length; i++){ // Loop through array y
if(y[i] != y[i+1]){ //check if value is single
newArr.push(y[i]); // then push it to new Array
}else{
i++; // else skip to next value which is same as y[i]
}
}
console.log(newArr);
If you check newArr it has value of:
[3, 4, 5]
var arr = [1,2,3,5,2,1,4]
var sorted_arr = arr.slice().sort(); // You can define the comparing function here.
var nonduplicates = [];
var duplicates=[];
for (var i = 0; i < arr.length; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
duplicates.push(sorted_arr[i]);
}else{
if(!duplicates.includes(sorted_arr[i])){
nonduplicates.push(sorted_arr[i]);
}
}
}
alert("Non duplicate elements >>"+ nonduplicates);
alert("Duplicate elements >>"+duplicates);
I think there could exists option with Map.
function unique(array) {
// code goes here
const myMap = new Map();
for (const el of array) {
// save elements of array that came only once in the same order
!myMap.has(el) ? myMap.set(el, 1) : myMap.delete(el);
}
return [...myMap.keys()];
}
const array = [1,2,3,5,2,1,4];
//[11, 23, 321, 300, 50, 23, 100,89,300];
console.log(unique(array));

search for matching values in two arrays with repeated values in javascript

I have two arrays:
a = [2,2,3,0,6]
b = [6,3,2,2,0]
I am trying use for loop to match values and get the index of a in a new array c. How can we do this? Notice that there are multiple values which match and so I think the previous match must be skipped.
This is a proposal which respects the last index and looks further.
How it works:
It uses Array#map for iterating array b with a callback. map gets an own this space with an really empty object Object.create(null).
The callback has on parameter bb which is one element of `b.
Next is to find the element is in array a with a Array#indexOf and a fromIndex, based on the former searches. The former index is stored in the this object, as long as the result is not -1, because this would reset the fromIndex to zero.
If there is no this[bb] or a falsy value of this[bb] take zero as fromIndex.
Later, a found index is incremented and stored in this[bb].
At least, the index is returned.
var a = [2, 2, 3, 0, 6],
b = [6, 3, 2, 2, 0],
c = b.map(function (bb) {
var index = a.indexOf(bb, this[bb] || 0);
if (index !== -1) {
this[bb] = index + 1;
}
return index;
}, Object.create(null));
console.log(c);
Another solution could be first generate an object with all indices of a and use it in the iteration of b for returning the indices.
The example is a bit extended, to show what happen if there is no more than two indices (2) and one without being in a (7).
The content of aObj with all indices of a:
{
"0": [3],
"2": [0, 1],
"3": [2],
"6": [4]
}
var a = [2, 2, 3, 0, 6],
b = [6, 3, 2, 2, 0, 7, 2],
aObj = Object.create(null),
c;
a.forEach(function (aa, i) {
aObj[aa] = aObj[aa] || [];
aObj[aa].push(i);
});
c = b.map(function (bb) {
return aObj[bb] && aObj[bb].length ? aObj[bb].shift() : -1;
});
console.log(c);
As far I Understand, You can try this:
var a = [2,2,3,0,6];
var b = [6,3,2,2,0];
var c = new Array();
for(i = 0; i < b.length; i++)
{
for(j = 0; j < a.length; j++)
{
if(b[i] === a[j] && c.indexOf(j) < 0)
{
c.push(j);
break;
}
}
}
console.log(c); // [4, 2, 0, 1, 3]
FIDDLE DEMO HERE
If I understand correct.
let c = a.map(i => b.indexOf(i))
or
var c = a.map(function(i) { return b.indexOf(i); });
loop .map function and check same value by indexOf
indexOf will return a number,representing the position where the specified search value occurs for the first time, or -1 if it never occurs
var arr = [];
a.map(function(v){
if(b.indexOf(v) > -1){
arr.push(v);
}
});
console.log(arr);
try something like this
var a = [2,2,3,0,6];
var b = [6,3,2,2,0];
var arrayLength_a = a.length;
var arrayLength_b = b.length;
var new_array=[];
for (var i = 0; i < arrayLength_a; i++)
{
for (var j = 0; j < arrayLength_b; j++)
{
if (a[i] == b[j])
{
if(new_array.indexOf(a[i]) === -1)
{
new_array.push(a[i]);
}
}
}
}

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