I am going through the grokking algorithms book and trying to wrap my head around recursion. One of the challenges in the book is to "Write a recursive function to count the number of items in a list.". I came up with the following code, which works:
function recursiveArrayCount(arr, count) {
if (arr.length == 0) {
return 0;
} else {
arr.pop();
return count + recursiveArrayCount(arr, count);
}
}
let myArray = [1, 10, 23, 11, 4, 48, 88];
console.log(recursiveArrayCount(myArray, 1));
My question is, is there a better way to do this in javascript? In particular, I don't like having to seed the value of count with the initial '1' - but I can't think of another way to do it.
You don't need a second argument at all:
function recursiveArrayCount(arr) {
if (arr.length == 0) {
return 0;
}
return 1 + recursiveArrayCount(arr.slice(1));
}
Make a proper tail call by eliminating any reference to variables that would be needed after the recursive call returns.
function recursiveArrayCount(arr) {
return _recursiveCount(arr, 0);
function _recursiveCount(arr, count) {
return arr.length == 0 ? count : _recursiveCount(arr.slice(1), count + 1);
}
}
let myArray = [1, 10, 23, 11, 4, 48, 88];
console.log(recursiveArrayCount(myArray));
This makes it more likely to be optimized by reusing stack space.
Also, I used a nested function for the recursion, which ensures that the count is properly initialized.
You could also get a little fancy with the inner function, like this:
function recursiveArrayCount(arr) {
return (function _recursiveCount(arr, count) {
return arr.length == 0 ? count : _recursiveCount(arr.slice(1), count + 1);
})(arr, 0);
}
let myArray = [1, 10, 23, 11, 4, 48, 88];
console.log(recursiveArrayCount(myArray));
It's a recursively invoked IIFE.
Related
sorry for the noob question probably, but I can't get my function to work. For me it looks very similar to the resolutions found on the web, but somehow it doesn't work and I can't tell where is the problem. Would be grateful for any help
function findvalue() {
var i = 0;
var array = [];
var min = array[0];
for (i = 0; i < array.length; i++) {
if (min > array[i]) {
min = array[i];
}
}
return min;
}
console.log(findvalue(11, 12, 13, 21, 22, 23, 97, 98, 99))
;
You could use arguments, an array like object of the function.
function findvalue() {
var i = 0,
min = arguments[0];
for (i = 1; i < arguments.length; i++) {
if (min > arguments[i]) {
min = arguments[i];
}
}
return min;
}
console.log(findvalue(11, 12, 13, 21, 22, 23, 97, 98, 99));
A shorter approach could be the use of rest parameters ... and spread syntax ... for the values for Math.min.
function findvalue(...args) {
return Math.min(...args)
}
console.log(findvalue(11, 12, 13, 21, 22, 23, 97, 98, 99));
Your function definition is incorrect, as well as how you are calling your function.
You are looking to iterate over an array, but you are calling your function with a bunch of numbers as the arguments. You instead need 1 parameter (argument) to call your function, which should be an array .
You have to instead call it this way:
findvalue([11, 12, 13, 21, 22, 23, 97, 98, 99])
Your function definition needs to be:
function findvalue(array) {
var i = 0;
var min = array[0];
for (i = 1; i < array.length; i++) {
if (min > array[i]) {
min = array[i];
}
}
return min;
}
As noted in the comments, you could modify your function definition to retain your initial way of calling the function. This is done by using rest parameters
The MDN docs describe rest parameters as:
The rest parameter syntax allows us to represent an indefinite number
of arguments as an array.
Call the function as you did: findvalue(11, 12, 13, 21, 22, 23, 97, 98, 99)
Your function definition would be:
function findvalue(...array) {
var i = 0;
var min = array[0];
for (i = 1; i < array.length; i++) {
if (min > array[i]) {
min = array[i];
}
}
return min;
}
You can use Math.min
function findMin() {
// arguments is an Array-like object corresponding to the arguments passed to a function.
return Math.min.apply(Math, arguments)
}
console.log(findMin(2,4,1,0,9,-2));
The missing thing in your function is the array must be a parameter of your function.
As you wrote it, the function is always trying to find the minimum in an empty array.
It is currently completely ignoring the example values you passed when you called your function.
So, instead of writing var array = [] in the body of you function, you have several possibilities :
1st possibility : take the array as parameter : declare your function as function(array) and change your call to actually pass an array of values : findValues([11, 12, 13, 21 ...]), and remove the var array = [] inside the body of your function.
2nd possiblity (my favorite): just replace var array = [] by var array = [...arguments]
Documention on the arguments object here : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
(and also, please note that let is now best practice than var)
See Nina 's answer for full snippets examples with arguments
try like this
function findvalue() {
var order = Array.from(arguments).sort(function(a,b){ return a - b; });
var min = order[0];
//var max = order[order.length-1];
return min;
}
// MIN value
console.log(findvalue(23, 97, 98, 99, 11, 12, 13, 21, 22));
I am sure the Arrow function will simplify your work.
//Variable diclaration
var numbers = [11, 12, 13, 21, 22, 23, 97, 98, 99];
//Arrow Function to find Min Number
var minfinder = numbers.reduce((a, b) => Math.min(a, b));
//Consloe Output
console.log("Min Number is: " + minfinder);
function getIndexToIns(arr, num) {
var index;
var sortedArr = arr.sort(function(a, b) {
return a - b;
});
for (var i = 0; i < sortedArr.length; i++) {
if (sortedArr[i] > num) {
index = sortedArr.indexOf(sortedArr[i]);
}
}
return index;
}
console.log(getIndexToIns([10, 20, 30, 40, 50], 35)); //returns 4?
console.log(getIndexToIns([10, 20, 30, 40, 50], 30)); //also returns 4?
To me, it should return index 3, not 4, as the number in index 4 is 50, and my num variable is 35, which is smaller than 40(at index 3). Is my logic wrong? I can't seem to figure out why.
In the second one, it is also returning index 4, when the index should be index 2?
You need to break out of your loop:
for (var i=0 ; i< sortedArr.length; i++){
if (sortedArr[i] > num){
index = sortedArr.indexOf(sortedArr[i]);
break;
}
}
Your loop will run even after it finds the first valid index. You can return index inside the loop to break, or call break yourself, etc.
Issue Correction
Here's a few of your issues:
var index; the variable is unnecessary since this is a simple function and you can return from the for-block. That's not completely bad practice as it's good to return a variable in more complicated functions
var sortedArr start using let and using a transpiler/packer if needed
arr.sort(function(a, b) { shorten with arrow functions (again transpiler/packer is your friend)
for (var i = 0; i < sortedArr.length; i++) { cache your .length into a variable for a minor performance bump
Example: for(var i=0,n=sortedArr.length; i<n; i++)
if (sortedArr[i] > num) {
index = sortedArr.indexOf(sortedArr[i]);
}
}
because you're not returning index from within the loop, the index would be overwritten with every subsequent true condition. In this case, the first true pass would be 40 and index would be set to 3, but then the loop would iterate again and come across 50, which is also bigger than your num, so index would be set to 4 and eventually to be returned
so many unnecessary curly braces
You have an inherit issue that you are returning the index of your sorted array and not your passed-in array. This means, that the index that's returned no longer corresponds with the argument. Maybe you didn't notice this because the argument you've passed in appears to already be sorted, but this could be a major silent error if your array was not pre-sorted and you were looking for the index within it.
A better way to write it:
if( sortedArr[i] > num )
return arr.indexOf( sortedArr[i] )
The following includes some quick helper functions (e.g., log) for formatting the log output. It is unnecessary, but a quick way to visually scan if you received the correct output for what you expected. Additionally a generic (basic) numeric sort function was added to make the code more readable. I find that function completely basic, but in your code you were re-defining the function with each call to your getIndexToIns, which is an unnecessary performance penalty.
Better Browsers
const getIndexToIns = (arr, num) => arr.indexOf(arr.sort(numeric).find(v => v > num))
log('getIndexToIns([10, 20, 30, 40, 50], 40)', 4)
log('getIndexToIns([10, 20, 30, 40, 50], 35)', 3)
log('getIndexToIns([10, 20, 30, 40, 50], 30)', 3)
function numeric(a, b) {
return a - b
}
function log(s, ex) {
console.log(`${s} = ${Function('return ' + s)()}`, ` // expected: ${ex}`)
}
Internet Explorer
log('getIndexToInsIE([10, 20, 30, 40, 50], 40)', 4)
log('getIndexToInsIE([10, 20, 30, 40, 50], 35)', 3)
log('getIndexToInsIE([10, 20, 30, 40, 50], 30)', 3)
function getIndexToInsIE(arr, num) {
return (
arr.indexOf(arr.sort(numeric).filter(function(v) {
return v > num
}).shift())
)
}
function numeric(a, b) {
return a - b
}
function log(s, ex) {
console.log(`${s} = ${Function('return ' + s)()}`, ` // expected: ${ex}`)
}
A functional alternative to consider :)
This will return the index of the first element in the array arr that is strictly larger than the number num:
function getIndexToIns(arr, num) {
return arr.findIndex(e => e > num);
}
I'm trying to handroll a solution for poker. I've gotten all the logic to determine the best 5 card hand. I'm having issues comparing multiple arrays for which have higher elements in the event of ties(with regard to hand type).
Say we have some potential winners for a flush.
var low_flush = [2, 3, 4, 5, 7]
var medium_flush = [3, 4, 5, 6, 8]
var high_flush = [2, 3, 4, 5, 9]
I want to build a function that i pass any number of arrays to and it returns the "highest poker hand" or an array of hands in the event of an actual tie:
function bestHand(hands){
//
return high_hand
}
Everything I've read thus far is how to compare just two arrays, and usually with that, it only sees if there are equal to each other. If it helps heres my source code
My first thoughts are to iterate over the hands. And for each iteration, iterate over the hands again for comparison. Just thinking about this pseudocode is making my head hurt and was thinking their might be a more elegant solution for this and/or library(not a poker library though)
I am using underscore in other parts of the codebase so feel free to use it in answer as well!
You can use reduce() first to count the sum of each array
var total3 = high_flush.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
}, 0);
after that push each one to an array with total of array and the name of the winner
allhands.push({total: total1 , name: "low_flush"});
then compare them with compare function and sort you array
function compare(a,b) {
if (a.total < b.total)
return -1;
else if (a.total > b.total)
return 1;
else
return 0;
}
allhands.sort(compare);
Working example here:
var low_flush = [2, 3, 4, 5, 7];
var medium_flush = [2, 3, 4, 5, 8];
var high_flush = [2, 3, 4, 5, 9];
var allhands = [];
var total3 = high_flush.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
}, 0);
allhands.push({total: total3 , name: "high_flush"});
var total1 = low_flush.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
}, 0);
allhands.push({total: total1 , name: "low_flush"});
var total2 = medium_flush.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
}, 0);
allhands.push({total: total2 , name: "medium_flush"});
function compare(a,b) {
if (a.total < b.total)
return -1;
else if (a.total > b.total)
return 1;
else
return 0;
}
allhands.sort(compare);
console.log("The winner is "+allhands[allhands.length - 1].name +"With total:"+ allhands[allhands.length - 1].total );
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
This may seem silly. But this seems to work for me.
var hands = [ [2, 3, 4, 5, 7], [2, 3, 5, 9, 8], [2, 3, 4, 5, 9] ];
var values = [];
function bestHand(hands){
hands.forEach(function(arr, index) {
var temp = arr.slice();
values[index] = parseInt(temp.sort().reverse().join(''));
});
var max = Math.max.apply(Math, values);
return hands[values.indexOf(max)];
}
bestHand(hands);
Obviously you need to pass an array of hand objects to something like this.
function getBest(...hands){
return hands.sort((p,c) => p.weight() <= c.weight() ? -1:1)[hands.length-1]
}
when it comes to finding out the weight of a hand object in tie conditions it could be first determined by the hands color (spades beats all) and then by sum of the card values for clubs like 2,3,4,5,6,7,8,9,10,11(j),12(q),13(k),14(a). The sum of them all is 104 so for diamonds card values can be (104+2), (104+3), (104+4) etc.. for hearts you offset values by 208 and for spades by 312.
hand.prototype.weight = function(){
return this.reduce((p,c) => p.value + c.value)
}
Of course this will only handle the tie conditions. It can not tell a flush from a flush royale.
Take a look at array's reduce function here.
Example:
var result = hands.reduce(function(prevHand, currentHand) {
return compareHands(prevHand,currentHand) ? prevHand : currentHand;
});
This answers the question about comparing multiples arrays, but keep in mind that it will reduce it to one value, this may not be the perfect solution since you have to consider draws. In that case your result should be an array, and in the comparison push into the array or reinitialize and add it to the array.
function bestHands(DescOrderedHands){
bestHand = DescOrderedHands[0]
bestHandScore = parseInt(_.map(bestHand, function(num){return num.toString}).join(""))
_.each(DescOrderedHands, function(hand){
stringHand = _.map(hand, function(num){return num.toString}).join("")
if (parseInt(stringHand) > bestHandScore){
bestHand = hand
}
}
return bestHand
}
First, You have to be sure that all arrays in your your function have the same length, then you have to sort each array, and finally you have to compare each value of each array.
You should have something like this
function bestHand(hands){
var best_hand = null;
for(var i = 0; i < hands.length; ++i){
hands[i] = hands[i].sort();
if(i > 0){
for(var j = 0; j < hands[i].length; ++j){
if(best_hand[j] < hands[i][j]){
best_hand = hands[i];
break;
}
else if(best_hand[j] > hands[i][j]){
break;
}
}
}
else{
best_hand = hands[i];
}
}
return best_hand;
}
Be sure you pass arrays with the same length in argument
(1)
I have here an array that is a mix of strings and ints:
var newArr = [ 22, 7, "2761", 16, "91981", "37728", "13909", "247214", "8804", 6, 2 ]
My ultimate goal is to remove the values that are ints, and add the other values.
One way I tried to achieve this was to first convert all to ints:
for (element in newArr) {
newArr[element] = parseInt(newArr[element], 10);
}
Is this the best way to do that?
It seems to output an array of ints like this:
var newArr = [ 22, 7, 2761, 16, 91981, 37728, 13909, 247214, 8804, 6, 2 ];
(2)
Then I would like to only sum elements in newArr that are above the value of 30.
This is my code:
var newArr = [ 22, 7, 2761, 16, 91981, 37728, 13909, 247214, 8804, 6, 2 ];
for (element in newArr) {
if (newArr[element] > 30) {
sum += newArr[element];
}
}
It doesn't seem to be working. Please help.
(3)
A final question is:
How could I just eliminate the ints from newArr in step (1), as this would negate the need for the other code, I guess.
A simple solution using only javascript syntax (no jquery) would be appreciated.
(unless the overwhelming consensus is that jquery would be a better option)
Thanks Javascript Ninjas!
First, you might want to look into Array.map. This will allow you to convert them all to ints.
var result = newArr.map(function(x) {
return parseInt(x, 10);
});
Then you can use Array.filter to remove any elements less than or equal to 30.
result = result.filter(function(x) {
return x > 30;
});
Finally, you can use Array.reduce to sum them all together.
sum = result.reduce(function(sum, x) {
return sum + x;
}, 0);
There are numerous ways of accomplishing this but I like this approach because you can chain it together.
var sum = newArray.map(function(x) {
return parseInt(x, 10);
})
.filter(function(x) {
return x > 30;
})
.reduce(function(sum, x) {
return sum + x;
}, 0);
Detect the type then remove them: Here I go through the array, then remove the numbers then go through it again:
var newArr = [22, 7, "2761", 16, "91981", "37728", "13909", "247214", "8804", 6, 2];
for (element in newArr) {
alert(typeof newArr[element] + ":" + newArr[element]);
}
for (var i = newArr.length; i--;) {
if (typeof newArr[i] === "number") {
newArr.splice(i, 1);
}
}
alert(newArr.length);
for (element in newArr) {
alert(typeof newArr[element] + ":" + newArr[element]);
}
I think the best approach to do all this in single iteration would be .
Checking each element with typeof item!="number"
Using Array's Reduce Right :
Sample Use:
[0, 1, 2, 3, 4].reduceRight(function(previousValue, currentValue, index, array) {
return previousValue + currentValue;
});
You can check the previousValue and currentValue with typeof and return the addition if parseInt() return's 30 or more.
Consider this example:
var a = {
"Check" : function doThis(value, index, ar) {
if(value >= 10) {
document.write(value + ", ");
}
else {
document.write("The array element is less than 10, ");
}
}
}
var arr = [12, 20, 2, 3, 15];
document.write(arr.forEach(a.Check));
Which results in:
12, 20, The array element is less than 10,
The array element is less than 10, 15, undefined
I don't understand why there is an extra element in the array which is undefined. Does it have something to do with defining the callback function in an object?
replace:
document.write(arr.forEach(a.Check));
with:
arr.forEach(a.Check);
With document.write(arr.forEach(a.Check)); you are printing what the arr.forEach() call returns (undefined)