Delete only one of the duplicated elements in ArrayList - javascript

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;
}

Related

Remove particular array elements and push them to the back of the array [duplicate]

This question already has answers here:
Move zeroes to end of array in javascript - how to return nothing?
(21 answers)
Closed last year.
Trying to remove all 0's from an array and return them in the same array
So for example if I have this as an array
let arrThree = [9,0,9,1,2,1,1,3,1,9,0,0,9,0,0,0,0,0,0,0]
I would get this:
let arrThree = [9,9,9,1,2,1,1,3,1,9,9,0,0,0,0,0,0,0,0,0,0,0]
This is what I wrote:
var remove = function (arr) {
let test = [];
for(let i = 0; i < arr.length; i++){
arr[i] === 0 ? test.push(arr.splice(i,1)) : false //if i is 0 then we want to push that into another array
}
arr.push(...test)
return [].concat(...arr)
}
When I run this function I get this
[
9, 9, 1, 2, 1, 1, 3,
1, 9, 0, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0
]
Not sure where I am going wrong?
You need only two iterations, one for finding non zero values and another to put zeroes to the right side until end of the array. You need no array pushing or splicing.
const
move0 = array => {
let i = 0,
j = 0;
while (i < array.length) {
if (array[i]) array[j++] = array[i];
i++;
}
while (j < array.length) {
array[j] = '#'; // in reality it is zero
j++;
}
},
array = [9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0];
move0(array)
console.log(...array);
If you don't need to have any sorting on the non-zero elements, a rather efficient solution would be to swap the left-most 0 with the right-most non-zero element. Once your indices for tracking where you are on the left and right cross you'll know you're done.
function moveZeros(arr) {
let i = 0;
let j = arr.length - 1;
while(i < j) {
// Found a 0 to the left of a non-zero, swap.
if(arr[i] == 0 && arr[j] != 0) {
let tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
// Find a zero
if(arr[i] != 0) {
i++;
}
// Find a non-zero
if(arr[j] == 0) {
j--;
}
}
return arr;
}
console.log(moveZeros([1,2,0,3,0,0,0]));
console.log(moveZeros([9,0,9,1,2,1,1,3,1,9,0,0,9,0,0,0,0,0,0,0]));
The issue with your code is you change the length of arr with arr.splice(i,1). This messes up the loop condition i < arr.length.
To fix your code, you can loop backwards through the array. So as the length of the array shortens, i is still valid.
let arrThree = [9,0,9,1,2,1,1,3,1,9,0,0,9,0,0,0,0,0,0,0];
var remove = function(arr) {
let test = [];
for(let i = arr.length - 1; i >= 0; --i) {
if (arr[i] === 0) test.push(arr.splice(i, 1)[0]);
}
return arr.concat(test);
}
console.log(remove(arrThree));
There are a few other ways to do this. One is to use filter to create 2 arrays - one with no 0 and one with all 0 and join them:
let arrThree = [9,0,9,1,2,1,1,3,1,9,0,0,9,0,0,0,0,0,0,0];
let remove = (arr) => arr.filter(i => i != 0).concat(arr.filter(i => i == 0));
console.log(remove(arrThree));

Move all zeroes toward the end -- can't get splice to work properly?

I'm trying to create a function that moves all 0s towards the end. I'm first counting how many zeroes are in the original array using a for loop; and also splicing the 0s using their indexes.
Then through a while loop, push 0s to the array based on the total count.
I'm struggling with trying to get the splicing to work.. I'd appreciate any input I could get.
const array = [2, 0, 0, 4, 3, 1];
const moveZeroes = (arr) => {
let zeroes = 0;
for(i=0;i<=arr.length;i++){
if(arr[i] === 0){
arr.splice(i, 1);
zeroes++;
}
}
while(zeroes > 0){
arr.push(0);
zeroes--;
}
console.log(arr);
};
moveZeroes(array);
so it's a common overseen bug that you are modifying the array on which you are iterating,
so try to print the array after every iteration of for loop and you will see that your index is incrementing but your array is decreasing in the length itself, which is causing this behavior
splice is working correctly BTW.
You can use sort() method.
const arr = [0, 0, 0, 0, 1, 0, 1];
const newArr = arr.sort((a, b) => b - a)
console.log(arr)
You just need to reduce the value of the i every time you splice the array.
function moveZero(array){
var numberofZeros = 0;
for(var i=0; i< array.length; i++){
if(array[i] == 0){
array.splice(i,1);
numberofZeros++;
i--;
}
}
while(numberofZeros > 0){
array.push(0);
numberofZeros--;
}
}
To optimize this I think you can use the sort method as answered by Vaibhav
If you find you do not have a requirement to modify the array in-place. You can create the array you are looking for like so:
const array = [0, 0, 0, 0, 1];
const moveZeroes = (arr) => {
const zeroes = 0;
const arrayWithoutZeros = array.filter(x => x !== zeroes);
const zerosRemovedCount = array.length - arrayWithoutZeros.length;
const output = arrayWithoutZeros.concat(
...Array(zerosRemovedCount).fill(zeroes)
);
console.log(output);
return output;
};
moveZeroes(array);
In-place sort solution:
const array = [2, 0, Infinity, 0, 77, 0, -454, 0, 1, 100, -4, 0, -2, 88 ];
const moveZeroes = (arr) => {
const zeroes = 0;
arr.sort((a, b) => {
if (a === zeroes) {
return 1;
}
if (b === zeroes) {
return -1;
}
return 0;
});
console.log(arr);
};
moveZeroes(array);
You can do that in one sweep. splice() is more expensive as it has to move all the elements after the section you've spliced and resize the array, and that for every zero you remove. This code costs exactly 1 write operation for each element in the array.
const array = [1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0];
const moveZeroes = (array) => {
let writeIndex = 0;
for (let readIndex = 0; readIndex < array.length; ++readIndex) {
if (array[readIndex] !== 0) {
array[writeIndex++] = array[readIndex];
}
}
while (writeIndex < array.length) {
array[writeIndex++] = 0;
}
return array;
}
console.log(moveZeroes(array));

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));

Javascript function. What is missing?

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.");

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