Counting number of occurrences recursively - javascript

Why is this recursive countOccurence function not working? this has a subroutine. is there a way to do it without a subroutine? it seems in javascript you have to have a closure (subroutine function) for the counter variable, otherwise it gets rewritten every time!
function numOccurencesRecursive(arr, val) {
//base case. check it if it only has a length of 1
var count = 0;
function doCount(arr, val) {
if (arr[0] === val) {
count++;
} else {
count += doCount(arr.slice(1), val)
}
return count;
}
return doCount(arr, val);
}
console.log(numOccurencesRecursive([2, 7, 4, 4, 1, 4], 4)); // should return 3 but returns 1

I think the problem is that you were thinking iteratively but used a recursive approach.
The iterative approach has a global variable which may be updated at each step:
function numOccurencesIterative(arr, val) {
var count = 0;
for(var i=0; i<arr.length; ++i) if(arr[i] === val) ++count;
return count;
}
However, when using recursive approaches, better avoid global variables.
function numOccurencesRecursive(arr, val) {
if(!arr.length) return 0;
return (arr[0] === val) + numOccurencesRecursive(arr.slice(1), val);
}

This should definitely do with all the nests with recursion:
function countItems(arr, item) {
var count = 0;
for (var i=0; i<arr.length; i++){
if (typeof(arr[i]) == typeof([])){
count += countItems(arr[i], item);
}
else{
if(typeof(arr[i]) != typeof([])){
if (arr[i] === item){
++count;
}
}
}
}
return count;
}

doCount stops recursing once it finds a match; hence, it will never find more than 1 match to count.

So, what you are doing is that you are incrementing the count only when you find the value, and when you do find it, your recursive function ends, but its the other way around, which means you have to count for unfound elements in the array and if you find something, increment it and then if the array is empty, return the count.
Code:
function numOccurencesRecursive(arr, val) {
//base case. check it if it only has a length of 1
var count = 0;
function doCount(arr, val) {
if (arr[0] === val) {
count++;
} else if (!arr.length) {
return count;
}
return doCount(arr.slice(1), val);
}
return doCount(arr, val);
}
console.log(numOccurencesRecursive([2, 7, 4, 4, 1, 4], 4)); // should return 3 but returns 1

Related

Return index of last occurrence in array

I have been working on this for a while now and I have no idea why this isn't working. I've read over it a few times and I can't seem to find the problem in my code.
I thought if(arr[i] === item) would check if the current element is equal to the item parameter and if true, return the index.
const lastIndexOf = (arr, item) => {
if(arr.includes(item)){
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
} else if (arr.includes(i) === false){
return Number('-1');
}
}
} else if (!arr.length){
return Number('-1');
} else {
return Number('-1');
}
}
console.log(lastIndexOf([5,5,5,5,0],5));
Remove the else if branch, as it is checking if the array contains the current index, which is nonsensical. Also, just use -1 instead of Number('-1').
const lastIndexOf = (arr, item) => {
if(arr.includes(item)){
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
}
}
return -1;
} else {
return -1;
}
}
console.log(lastIndexOf([5,5,5,5,0],5));
You are currently passing through the array twice, once using Array#includes and once with your for loop, which is suboptimal. You can simply loop over the array backwards once, like you are currently doing, and return -1 at the end of the function, since it will only reach there if the item has not been found.
const lastIndexOf = (arr, item) => {
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
}
}
return -1;
}
console.log(lastIndexOf([5,5,5,5,0],5));
The Array#lastIndexOf method already exists, so there is no need to reinvent the wheel, unless you are trying to make a polyfill.
console.log([5,5,5,5,0].lastIndexOf(5));
The problem is here:
} else if (arr.includes(i) === false){
You're asking if any of the array members equals the current index. Because 4 (your first checked index) is not accounted for in that array, it returns.
Seems like you don't need that else if at all.
const lastIndexOf = (arr, item) => {
if(arr.includes(item)){
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
}
}
} else {
return -1;
}
}
console.log(lastIndexOf([5,5,5,5,0],5));
Also, the last, outer else if and else branches yield the same result, so the else if is extraneous, so I removed it.
And Number('-1') is an unnecessarily roundabout way of returning -1.
And actually, your .includes() check seems redundant. It's going to have to iterate before you iterate. Might as well just do it once.
const lastIndexOf = (arr, item) => {
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
}
}
return -1;
}
console.log(lastIndexOf([5,5,5,5,0],5));

Sum all numbers in array function not returning the sum

I am trying to create a function that sum all numbers in an array! I am new to JavaScript and in need of someone pointing me in the right direction!
function sum(arr) {
var i = 0;
for (var index = 0; index < arr.length; index++) {
return index += arr[i];
}
}
sum([1, 2, 3]); //6
Lots of basic issues with the code.
You need a separate variable to accumulate the result in. Your code is writing into the index variable of a for loop. This is wrong for a number of reasons.
You also return after the first iteration of the loop.
Assuming you want to use a for loop (which is not the least amount of code),
function sum(arr) {
var sum = 0;
for (var index = 0; index < arr.length; index++) {
sum += arr[index];
}
return sum;
}
Use Array.reduce() for that:
function getSum(ary){
return ary.reduce(function(sum, value) {
return sum + value;
}, 0);
}
console.log(getSum([0, 1, 2, 3]));
to illustrate where your code is wrong
function sum(arr) {
var i = 0;
for (var index = 0; index < arr.length; index++) {
return index += arr[i]; // this will return from your function in the first iteration
}
}
as the comment says, return will exit your function in the first iteration
also, you're adding to index, which is supposed to be the index into the array, you want to add to i, and then return i after the loop
so, the code should be
function sum(arr) {
var i = 0;
for (var index = 0; index < arr.length; index++) {
i += arr[index];
}
return i;
}
As another answer pointed out, a probably better alternative is to use array reduce function - however the code in that answer is not the "best" usage of reduce
function getSum(ary){
return ary.reduce(function(sum, value) {
return sum + value;
}, 0);
}
can actually be written
function getSum(ary){
return ary.reduce(function(sum, value) {
return sum + value;
});
}
This uses one less iteration, because there is no "initial value", and the first iteration adds index 0 and 1 together
Sure, it's not going to make a performance difference, but why not use built-in functions properly :p
function Sum(arr) {
var sum = 0;
for (var index = 0; index < arr.length; index++) {
sum += arr[index];
}
return index;
}
Sum([1, 2, 3]); //6
Return immediately exits a function. Your code will never sum values. What is 'i' in your code!?! And what index do!?
Here's how you get your function to work:
function sum(arr) {
// this is the variable we're going to add our numbers onto
var acc = 0;
for (var index = 0; index < arr.length; index++) {
// don't return here, return is used to return from a function
acc += arr[index];
}
// after adding all numbers, return the sum
return acc
}
sum([1, 2, 3]); //6
But there are built in ways to do this, like Array.reduce() like Scott Marcus mentioned.

Finding first duplicate in an array and returning the minimal index

So the question reads:
Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index. In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of the other number does. If there are no such elements, return -1.
Write a solution with O(n) time complexity and O(1) additional space complexity.
I have a solution, but apparently it's not fast enough and stalls when there are over a thousand items in the array.
This is what I have:
function firstDuplicate(arr) {
let dictionary = {};
for(let i = 0, ii = arr.length; i < ii; i++) {
for(let z = i+1, zz = arr.length; z < zz; z++) {
if(arr[i] === arr[z]) {
if(dictionary.hasOwnProperty(arr[i])) {
if(dictionary[arr[i]] !== 0 && dictionary[arr[i]] > z) {
dictionary[i] = z;
}
} else {
dictionary[arr[i]] = z;
}
}
}
}
let answer = [];
for(key in dictionary) {
// [array number, position];
answer.push([key, dictionary[key]]);
};
if(answer.length > 0) {
return Number(answer.sort((a, b) => {
return a[1]-b[1];
})[0][0]);
}
return -1;
}
I think converting the object into an array and then sorting the array after the answers are complete slows down the whole function. Using built in JS methods like forEach, map and sort (like I did above), slows the code/function down even more. There is obviously a better and more accurate way to do this, so I'm asking for some JS masterminds to help me out on this.
you can keep adding numbers to a dictionary as keys with values as their index, and as soon as you find a repeating key in the dictionary return its value. This will be O(n) time complexity and O(n) space complexity.
function firstDuplicate(arr) {
var dictionary = {};
for(var i = 0; i < arr.length; i++) {
if(dictionary[arr[i]] !== undefined)
return arr[i];
else
dictionary[arr[i]] = i;
}
return -1;
}
console.log(firstDuplicate([2, 3, 3, 1, 5, 2]));
Since the numbers are between 1 to arr.length you can iterate on the array. For each arr[i] use arr[i] as index and make the element present and arr[arr[i]] negative, then the first arr[arr[i]] negative return arr[i]. This give O(1) space complexity and O(n) time complexity you can do this:
function firstDuplicate(arr) {
for(var i = 0; i < arr.length; i++) {
if(arr[Math.abs(arr[i])] < 0)
return Math.abs(arr[i]);
else
arr[Math.abs(arr[i])] = 0 - arr[Math.abs(arr[i])];
}
return -1;
}
console.log(firstDuplicate([2, 3, 3, 1, 5, 2]));
function firstDuplicate(arr) {
for(var i = 0; i < arr.length; i++) {
var num = Math.abs(arr[i]);
if(arr[num] < 0)
return num;
arr[num] = - arr[num];
}
return -1;
}
console.log(firstDuplicate([2,2,3,1,2]));
function firstDuplicate(arr) {
var numMap = {};
for (var i = 0; i < arr.length; i++) {
if (numMap[arr[i]]) {
return arr[i];
}
numMap[arr[i]] = true;
}
return -1;
}
Answer mentioned by #dij is great, but will fail for [2,2] or [2,3,3],
a little change
for input [2,2], i=0 we get a[ Math.abs[a[0] ] = a[2] = undefined
so we remove 1 from a[ Math.abs[a[0] -1 ] this will work now
function firstDuplicate(arr) {
for(var i = 0; i < arr.length; i++) {
if(arr[Math.abs(arr[i])-1] < 0)
return Math.abs(arr[i]);
else
arr[Math.abs(arr[i])-1] = 0 - arr[Math.abs(arr[i])-1];
}
return -1;
}
Please try the code below:
function getCountOccurence(A) {
let result = [];
A.forEach(elem => {
if (result.length > 0) {
let isOccure = false;
result.some((element) => {
if (element.element == elem) {
element.count++;
isOccure = true;
}
});
if (!isOccure) {
result.push({element: elem, count: 1});
}
} else {
result.push({element: elem, count: 1});
}
});
return result;
}
function getFirstRepeatingElement(A) {
let array = getCountOccurence(A);
array.some((element)=> {
if (element.count > 1) {
result = element.element;
return true;
}
});
return result;
}

How do you use a for loop inside of the callback function of the filter method?

This code returns the correct array but I am cheating inside of the arrayFilter function. By using matchItem[0] and matchItem[1] I am hard coding the use of only 2 filter items. If there are less than 2, the test will fail, and if there are more than 2, the extra items will not be filtered.
function destroyer(arr) {
// Remove all the values
var destroyerArgsAmount = arguments.length;
var matchArr = arguments[0];
var newArr = [];
var matchItems = [];
for(i = 1; i < destroyerArgsAmount; i++) {
matchItems.push(arr[i]);
}
function arrayFilter(val) {
if(val != matchItems[0] && val != matchItems[1]) {
return true;
}
}
arr = matchArr.filter(arrayFilter);
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
Why does a for loop not work in this situation?
for(i = 0; i < matchItems.length; i++) {
if(matchItems[i] != value) {
return value;
}
This loop does not work when placed into the arrayFilter function
Can someone explain to me why and then get me on the correct path to how this is done? I figure this will be an important thing to master since there are many methods employed this way like: filter, map, forEach and many more I don't know about yet.
I'm not exactly sure the results you're trying to achieve since you haven't explained it but, I'm assuming from your code your function is trying to filter all arguments passed after the first argument array. Like this:
function destroyer (arr) {
// You can use slice to grab all arguments after the first index in arguments
var args = Array.prototype.slice.call(arguments, 1);
return arr.filter(function (item) {
// filter anything not found in the arguments list
// indexOf will return -1 if not found
return args.indexOf(item) < 0;
});
}
What you seem to need is a way to check if a value is in an Array.
As mentioned by #adeneo, if val is a primitive value (string, number, etc) or an object that should be compared by reference, the solution is as simple as
matchItems.indexOf(val)
which returns a positive number (the index) if val is one of the items in matchItems and -1 otherwise.
If your use case is slightly more complicated, for example if matchItems contains objects and you want to see if an object with a particular property is in it, you can use Array.prototype.some:
var found = matchItems.some(function(item){
return item === val; // use the apropriate comparison here
});
Your full code would be:
function destroyer(arr) {
var matchItems = Array.prototype.slice.call(arguments, 1);
return arr.filter(function(a) {
return !matchItems.some(function(b){
return a === b;
});
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
Use indexOf and return the result
function arrayFilter(val) {
return matchItems.indexOf(val) === -1;
}
I would think maybe you're looking for something more like this
function destroyer() {
var args = [].slice.call(arguments),
matchArr = args.shift();
return matchArr.filter(function(item) {
return args.every(function(item2) {
return item !== item2;
});
});
}
FIDDLE
I'm also doing the free code camp's challenges! This code passed the challenge:
function destroyer(arr) {
var arr = arguments[0];
for (var i = 0; i < arr.length; i++) {
for (var j = 1; j < arguments.length; j++) {
if (arr[i] === arguments[j]) {
arr.splice(i, 1);
i--;
}
}
}
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);

Find if Array Contains 5 consecutive Numbers with Javascript

I have a sorted Array that contains numbers. I want to be able to check if this Array(or similar Array), contains 5 numbers in consecutive order.
NOTE: Array may contain duplicate and double digit numbers.
I am trying this, but failing epically.
var array = [1,3,5,7,7,8,9,10,11]
var current = null;
var cnt = 0;
for (var i = 0; i < array.length; i++) {
if (array[i] != current) {
if (cnt > 4) {
return true;
}
current = array[i];
cnt = 1;
} else {
cnt++;
}
}
if (cnt > 4) {
return true;
}
}
An iterative, straightforward approach would be:
var should_be_true = [1,3,5,7,7,8,9,10,11];
var should_be_false = [1,3,5,7,9,11,13,15,17];
var testArray = function(array) {
var conseq = 1;
for (var idx = 1; idx < array.length ; idx++) {
if (array[idx] == array[idx-1] + 1)
conseq++;
else
conseq = 1;
if (conseq == 5)
return true;
}
return false;
}
console.log(testArray(should_be_true)); //true
console.log(testArray(should_be_false)); //false
But for bonus fun, here's one variation on a functional approach, returning the position where the sequence starts, or -1 if no sufficiently long sequence exists:
should_be_true.map(function(curr,idx,arr) {
return (curr == arr[idx-1] +1) ? 1 : 0;
}).join('').search(/1{4}/);
A functional approach would be
function fiveInARow(array) {
// compare if one element is greater than or equal to the previous one
function compare(elt, i, arr) { return !i || elt >= arr[i-1]; });
// check if at a given position, every one of the last five comparisons is true
function check (_, i, greaters) {
return i >= 4 && greaters.slice(i-4, i) . every(Boolean);
}
return array . map(compare) . some(check);
}
The logic here is to first create an array of booleans using map, showing whether each element is greater than or equal to the previous. That yields an array such as [true, true, true, false, true].
The some part asks, for any element, is it the case that that element and every one of the preceding four elements are true? If so, it returns true.
Recursive solution
A recursive solution might be a bit easier to read.
function fiveInARow(array) {
return function _five(array, prev, n) {
if (n >= 5) return true;
if (!array.length) return false;
var next = array.shift();
return _five(array, next, next === prev ? n : next >= prev ? n+1 : 0);
}(array, -999999, 5);
}
var array = [1,3,5,7,7,8,9,10,11];
var cons=false;
var count=0;
for(i=0;i<array.length;i++){
if(array[i]+1==array[i+1]){
count++;
}
else{
count=0;
}
if(count==4){
cons=true;
}
}
if(cons){
//do what you want with it
}
DEMO
a more elegant way would be to define this whole thing in a function as below:
function checkCons(array){
var cons=false;
var count=0;
for(i=0;i<array.length;i++){
if(array[i]+1==array[i+1]){
count++;
}
else{
count=0;
}
if(count==4){
cons=true;
}
}
return cons;
}
and then using it like this:
var array = [1,3,5,7,7,8,9,10,11];
if(checkCons(array)){
//do what you want with it
}
DEMO
function fiveStraight() {
var array = [1, 3, 5, 7, 7, 8, 9, 10, 12];
var prev = array[0];
var numberOfStraight = 1;
for (var i = 1; i < array.length; i++) {
numberOfStraight = array[i] === prev + 1 ? numberOfStraight + 1 : 1;
prev = array[i];
if (numberOfStraight === 5) return true;
}
return false;
}
JSFIDDLE.
Here is what I found out to be the most straight forwards approach. Both descending and ascending values count as consecutive (if that's not your case, then fiddle with the Math.abs() call).
function consecutiveValuesCount(array) {
// sort the values if not pre-sorted
var sortedValues = array.sort();
// the result variable / how many consecutive numbers did we find?
// there's always atleast 1 consecutive digit ...
var count = 1;
for (var i = 0; i < sortedValues.length - 1; i++) {
// both descending and ascending orders count as we are using Math.abs()
if (Math.abs(sortedValues[i] - sortedValues[i+1]) == 1) {
++count;
}
}
return count;
}
//input
var array = [1,2,4,5,3];
// output
5
with this code you can find the highest number of consecutives for a given number or find the highest number of consecutives in general
var findMaxConsecutiveOnes = function (arr, number) {
//check for boundries
if(!number || !arr.length) return;
// maximum number of consectuives
let max = 0;
// count homy many consecutives before it ends (why it's 1 ? because a lonely number is still counted)
let counter = 1;
// you can ignore the next 2 variable if you want to use for loop instead of while
let length = arr.length;
let i = 1; // counting from index 1 because we are checking against index-1
while (i < length) {
if (arr[i] == arr[i - 1]) {
// boom, we have a consecutive, count it now
counter++;
} else {
// rest to 1
counter = 1;
}
// always update the max variable to the new max value
max = Math.max(counter, max);
//for the sake of iteration
i++;
}
return max== number;
};
console.log(findMaxConsecutiveOnes([5, 5, 5, 1, 1, 1, 1, 1]));
cnt will only increase once, when it hits the two 7s.
Put the incrementing line in the truthy condition, and the reset line in the else statement.
// Put into a function for testing.
function foo() {
var array = [1, 3, 5, 7, 7, 8, 9, 10, 11]
var current = null;
var cnt = 0;
for (var i = 0; i < array.length; i++) {
// Also need to make sure the next array item is a consecutive increase.
if (array[i] != current && array[i] === array[i-1] + 1) {
if (cnt > 4) {
return true;
}
current = array[i];
cnt++;
} else {
cnt = 1;
}
}
if (cnt > 4) {
return true;
} else {
return false;
}
};
// Call function.
alert(foo());

Categories