Compare 2 or more sorted arrays javascript - javascript

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

Related

Recursion in javascript, function restArray

Anyone can help me? i dont know where is my failure
Implement the restArray function: from an array in which each
position can be a single number or another nested array of numbers,
determine the sum of all numbers contained in the array.
The array will be received by parameter.
Example:
const array = [3, [7, [5,6]], [9,6], 4];
restArray(array); --> It should return 40 and you subtract the result by the number of arrays, not counting the parent.
Example:
40-3 = 37
var restArray = function(array) {
let sum = 0;
if (array.length === 0) {
return -1;
}
for (arr of array) {
if (!Array.isArray(arr)) {
sum += arr;
} else {
sum += restArray(arr);
}
}
return sum;
};
console.log(restArray([3, [7, [5,6]], [9,6], 4]));
Just a minor mistake in your code. As the top level array should not count for subtracting one from the sum, you should not have that case handled in the first if block.
Instead apply that minus one when coming back from recursion, so to account for that nested array:
var restArray = function(array){
let sum = 0;
for (arr of array) {
if (!Array.isArray(arr)) {
sum += arr;
} else {
sum += restArray(arr) - 1;
}
}
return sum;
};
const array = [3, [7, [5,6]], [9,6], 4];
console.log(restArray(array));
It is possible to use reduce to shorten the code a bit and use a bit more functional programming approach:
const restArray = array =>
array.reduce((sum, arr) =>
sum + (Array.isArray(arr) ? restArray(arr) - 1 : arr)
, 0);
const array = [3, [7, [5,6]], [9,6], 4];
console.log(restArray(array));

Split array into arrays of numbers where the sum is equal to a specific target

I need to create a function that take as parameter an array and a target. It should return an array of arrays where the sum of these numbers equals to the target
sumPairs(array, target) {
}
For example:
sumPairs([1, 2, 3, 4, 5], 7) // output : [[2, 5], [3, 4]]
I know I have to use map(), and probably reduce(), set(), or filter() maybe (I read their documentation in MDN but still cant find out). I tried some ways but I can't get it.
If you guys could help me to find out how to dynamically create arrays and push them into a new array..
I read there some solutions (Split array into arrays of matching values) but I hate to just use created functions without knowing what they really do or how they work.
Some very basic code for achieving it, Just run all over combinations and conditionally add the items you want.
function sumPairs(array, target) {
var res = [];
for(var i = 0; i < array.length; i++){
for(var j = 0; j < array.length; j++){
if(i!=j && array[i]+array[j]==target &&
res.filter((x)=> x[0] == array[j] && x[1] == array[i]).length == 0 )
res.push([array[i], array[j]]);
}
}
return res;
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
Option 2 - see this answer for more options (like using reduce)
function sumPairs(array, target) {
return array.flatMap(
(v, i) => array.slice(i+1).filter(w => (v!=w && v+w==target)).map(w=> [w,v])
);
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
"The exercise says that it sould be arrays of pairs that sum the
target value so I think only 2 items"
If you need a pair that matches a sum and you pick any number from the list, you are left with
the following equation to solve num + x = sum where we want to find x. E.g. if you picked 7 and the target sum is 10 then you know you are looking for a 3.
Therefore, we can first construct a counting map of the numbers available in our list linear (O(n)) time and then search for matches in linear time as well rather than brute forcing with a quadratic algorithm.
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
return nums.reduce((pairs, num) => {
countByNum[num]--;
const target = sum - num;
if (countByNum[target] > 0) {
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[num]++;
}
return pairs;
}, []);
}
function countGroupByNum(nums) {
return nums.reduce((acc, n) => (acc[n] = (acc[n] || 0) + 1, acc), {});
}
Here's another implementation with more standard paradigms (e.g. no reduce):
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
const pairs = [];
for (const num of nums) {
const target = sum - num; //Calculate the target to make the sum
countByNum[num]--; //Make sure we dont pick the same num instance
if (countByNum[target] > 0) { //If we found the target
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[target]++; //Didin't find a match, return the deducted num
}
}
return pairs;
}
function countGroupByNum(nums) {
const countByNum = {};
for (const num of nums) {
countByNum[num] = (countByNum[num] || 0) + 1;
}
return countByNum;
}
You can also sort your array and find all the pairs with given sum by using two pointer method. Place the first pointer to the start of the array and the second pointer to the end.
if the sum of the values at the two places is :
More than target: Decrement your second pointer by 1
Less than target: Increment your first pointer by 1
Equal to target: This is one possible answer, push them to your answer array and increment your first pointer by 1 and decrement your second pointer by 1.
This is more performant solution with complexity O(n*log(n))

How to get the sum of all duplicates in an array?

I am trying to calculate the sum of all duplicates in an array. For example:
duplicate([1,1,2,3,3]) --> should return 8.
I have written the following function to calculate the sum of duplicates in an array using JavaScript. Currently it is returning an array with duplicates one less than what they are present in the array.
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
stack.push(arr[i])
}
}
return stack;
}
console.log(duplicate([1,2,1,2,2,3,3]))
This is returning [ 1, 2, 2, 3 ]
How do I get the correct array and calculate the correct sum? I have to use Object for that?
To make the logic easier, you might filter out the non-duplicates by checking whether their indexOf in the array is equal to their lastIndexOf in the array:
function duplicate(arr) {
const duplicates = arr.filter(elm => arr.indexOf(elm) !== arr.lastIndexOf(elm));
return duplicates.reduce((a, b) => a + b);
}
console.log(duplicate([1,1,2,3,3])); // --> should return 8.
console.log(duplicate([1,2,1,2,2,3,3]));
Initially create an object where the keys will be the integer and their value will be the number of occurrence. Then if the number of occurrence is more than 1 , multiply the number with number of occurrence.
function duplicate(arr) {
let dupVal = 0;
let k = arr.reduce((acc, curr, index) => {
if (acc[curr] === undefined) {
acc[curr] = 1
} else {
acc[curr] += 1;
}
return acc
}, {});
for (let keys in k) {
if (k[keys] > 1) {
dupVal += parseInt(keys, 10) * k[keys]
}
}
return dupVal;
}
console.log(duplicate([1, 2, 1, 2, 2, 3, 3]))
Try This one
const arr = [1,1,2,3,3]
let dup = arr.filter((value, index)=>{
// creating a copy of main array
let copyarr = [].concat(arr)
// removing present value
copyarr.splice(index,1)
// after removing present value, if you still
// get the value in copied array that means
// it has duplicates
if(copyarr.indexOf(value)>-1){
return true
}
return false
})
// now add it using reduce
let sum = dup.reduce((acc, value)=> acc+value,0)
console.log(sum)
Copy above code and paste into chrome devTool. You will get the answer.
The problem is that you are matching value with immediate next value in array, in array that is sorted already it will work, but not on unsorted one. So try to sort the array first and then run your code.
Edit :
Looks like sorting is added in code,
But another condition => if there is number that is repeated more than twice it should be handled and only appear once in stack, if that is required.
This will : console.log(duplicate([1,2,1,2,2,3,3]))
Result this : [1,2,3]
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
if(stack.length == 0 || (arr[i] != stack[stack.length-1])){
stack.push(arr[i])
}
}
}
return stack;
}
you can use JS Array.reduce method to accomplish your requirement in a shorter way
function sumDplicates(arr) {
return arr.reduce(function(tot, val, index, _arr) {
if (_arr.lastIndexOf(val) > index || _arr.indexOf(val) != index)
return tot + val;
return tot
}, 0)
}
console.log(sumDplicates([1, 1, 2, 3, 3]));
console.log(sumDplicates([1, 2, 1, 2, 2, 3, 3]));
You can pursue your original sorting approach with a slight modification:
if (arr[i] === arr[i + 1] || arr[i] === arr[i - 1])
That is, check if the previous or the next element in the sorted array is equal to the current element for it to qualify as a duplicate.
The following solution accomplishes this with filter and reduce:
function duplicate(array) {
return array
.sort((a, b) => a - b)
.filter((a, i, arr) => (arr[i] === arr[i + 1] || arr[i] === arr[i - 1]))
.reduce((a, b) => a + b, 0);
}
console.log(duplicate([1, 1, 2, 3, 3]));
console.log(duplicate([1, 2, 1, 2, 3, 3]));
Array.reduce() and Array.lastIndexOf() will simply solve your problem.
function sum(arr)
{
return arr.reduce(function(sum, item){
return arr.lastIndexOf(item)!==arr.indexOf(item) ? sum+=item : sum;
},0)
}
console.log(sum([1,1,2,3,3]));
console.log(sum([1,2,3,4]));
console.log(sum([1,2,2,3,4]));
console.log(sum([1,1,2,2,3,3,4,4]));
Though I don't know much about JavaScript, If I were you, I would have simply kept a temporary array, which copies all the duplicate variables and then use that array for sum.
Also, if you want to add the particular number as many times as it appears, I will suggest creating a table like the one in sparse matrices
and then referring to it during addition.
This logic, though not space efficient, is very easy to implement.
Here is an approach with a single Array.reduce and nothing else. No Array.indexOf or Array.lastIndexOf. Although it might not be as concise it does not traverse the array looking for indexes multiple times nor does any Array.filter:
const sumDubs = arr => arr.reduce((r,c) => {
if(r[c]) {
r[c] += 1
r.sum += r[c] > 2 ? (r[c]*c) - ((r[c]-1)*c) : r[c]*c
} else r[c] = 1
return r
}, { sum: 0 }).sum
console.log(sumDubs([1, 1, 2, 3, 3])) // 8
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3])) // 14
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3, 3, 1, 2, 4, 4])) // 28
The idea is to keep track of the on-going sum via a property in the accumulator of the Array.reduce and simply keep calculating the sum based on which number is duplicated and more importantly how many times.

Javascript - Remove elements from an array / sum elements in an array above a certain value

(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.

Find highest amount of repeating value in an array using JavaScript

I have an array with repeating values:
[0, 1, 6, 0, 1, 0]
What is an efficient way to return the highest amount that a specific value repeats?
In the example array I would want the script to return 3 because the number 0 repeats the most often and it repeats 3 times.
I'm already using jQuery and Underscore.
If you use lodash...
_.max(_.countBy(a,_.identity))
More about lodash: http://lodash.com/
This is similar to the basic approach, but utilizes underscore's reduce and max functions. If you had a really really really large array, I hope it's clear how you could parallelize this with a map phase before the reduce.
var arr = [1,0,2,3,4,0,3,0];
var counts = _.reduce(arr, function(counts, val) {
if (counts[val]) {
counts[val]++;
} else {
counts[val] = 1;
}
return counts;
}, {});
return _.max(counts);
Yay, this is like a google interview question lol. I would recommend looping through your array once and maintaining an associative array of each element as you encounter it in the while incrementing a counter.
For example:
http://jsfiddle.net/btKjX/
var a = [0, 0 , 2, 2, 3, 3, 3, 3];
var counts = {};
for(var i = 0, il = a.length; i < il; i++){
var num = a[i];
if(typeof counts[num] === 'undefined'){
counts[num] = 0;
}
counts[num]++;
}
var max = -1;
for(var c in counts){
if(counts[c] > max){
max = counts[c];
}
}
console.log(max);
a hacky way might be to sort it, split it on every value change and then look at the length of each string, but let's try something more reasonable:
var nums = [0, 1, 6, 0, 1, 0]
var occurence = {}
$.each(nums, function(a, num_id) {
if (occurence[num_id] != null) {
occurence[num_id]++;
} else {
occurence[num_id] = 1;
}
});
occurence would then have the number of occurences of every value in nums, with the number itself being the key.

Categories

Resources