Determine if a function has two pairs - javascript

Trying to build a video poker app and I've got most of the winning logic done but I can not wrap my head around two pairs.
function isTwoPair() {
const tempHand = [...playerHand];
let reduceHand = tempHand.reduce((acc, curVal) => {
if (curVal.rank in acc) {
acc[curVal.rank]++;
} else {
acc[curVal.rank] = 1;
}
return acc;
}, {});
const sortedHand = Object.fromEntries(Object.entries(reduceHand).sort());
for (const [key, value] of Object.entries(reduceHand)) {
let pairs = 0;
if (value === 2) {
pairs++;
if (pairs === 2) {
return true;
}
}
}
}
My thought was to use reduce to determine the number of values each key has then sort it (ascending) and loop through it. If any value was equal to 2 then it would update the pairs variable by 1. Once pairs got to 2 then it would return true.
What is a better way, or the correct way I should say since this doesnt work, of finding two pairs in a given array of objects.
The deck is an array of objects that look like this:
[
{
card: "Ah",
rank: 14,
suit: "hearts",
img: "./images/hearts/hearts-A.svg",
isHold: false,
},
]

function isTwoPair() {
const reduceHand = reduceHandRank();
let pair = 0;
for (const [key, value] of Object.entries(reduceHand)) {
if (value === 2) {
pair++;
}
}
if (pair === 2) {
return true;
}
}
Determination will get you there! Hot dawg!!!

I'd count each card rank. If the count is more than 2, return false. After counting, filter the values that are 2 and return if its length is equal to 2
function isTwoPair() {
let count={}
for(let i=0;i<playerHand.length;i++){
let card=playerHand[i]
if(card.rank in count){
count[card.rank]+=1
if(count[card.rank]>2){
return false
}
}else{
count[card.rank]=1
}
}
return Object.values(count).filter(c=>c==2).length == 2
}
Full example:
let playerHand = [{
rank: 14,
suit: "spades",
img: "./images/hearts/hearts-A.svg",
isHold: false,
},
{
rank: 14,
suit: "hearts",
img: "./images/hearts/hearts-A.svg",
isHold: false,
},
{
rank: 14,
suit: "diamonds",
img: "./images/hearts/diamonds-A.svg",
isHold: false,
},
{
rank: 14,
suit: "clubs",
img: "./images/hearts/diamonds-A.svg",
isHold: false,
},
{
rank: 8,
suit: "hearts",
img: "./images/hearts/hearts-8.svg",
isHold: false,
},
]
function isTwoPair() {
let count={}
for(let i=0;i<playerHand.length;i++){
let card=playerHand[i]
if(card.rank in count){
count[card.rank]+=1
if(count[card.rank]>2){
return false
}
}else{
count[card.rank]=1
}
}
return Object.values(count).filter(c=>c==2).length == 2
}
console.log(isTwoPair())
playerHand = [{
rank: 14,
suit: "hearts",
img: "./images/hearts/hearts-A.svg",
isHold: false,
},
{
rank: 14,
suit: "spades",
img: "./images/hearts/hearts-A.svg",
isHold: false,
},
{
rank: 13,
suit: "clubs",
img: "./images/hearts/diamonds-A.svg",
isHold: false,
},
{
rank: 13,
suit: "diamonds",
img: "./images/hearts/diamonds-A.svg",
isHold: false,
},
{
rank: 8,
suit: "hearts",
img: "./images/hearts/hearts-8.svg",
isHold: false,
},
]
console.log(isTwoPair())

I would recommend passing in playerHand so your function can be used for multiple hands. If you're not familiar, ~~ has a side-effect of turning non-truthy values into 0, and leaving integers intact.
function isTwoPair(playerHand) {
let counts={};
playerHand.forEach(hand => counts[hand.rank] = ~~counts[hand.rank]+1);
let sorted=Object.values(counts).sort();
return sorted[sorted.length-1]==2 && sorted[sorted.length-2]==2
}

Related

Change values in object without changing parent object

I have a object with some (cards) values and I would like have another object which has values converted (cards by name) by another function.
cardsHand = [
{ value: "1", suit: "C" },
{ value: "13", suit: "C" },
{ value: "12", suit: "C" },
{ value: "11", suit: "C" },
{ value: "10", suit: "C" },
];
function sortCards(cardsHand) {
//SORT VALUES
let sortedCardsByValue = [];
sortedCardsByValue = cardsHand.sort(
(currCardA, currCardB) => currCardA.value - currCardB.value);
//CHANGE VALUES
let convertedCards = cardsHand;
convertedCards.forEach((element) => {
if (+element.value === 1) element.value = `A`;
if (+element.value === 11) element.value = `J`;
if (+element.value === 12) element.value = `Q`;
if (+element.value === 13) element.value = `K`;
});
return {
convertedCards,
sortedCardsByValue,
};
}
console.log(sortCards(cardsHand).convertedCards);
console.log(sortCards(cardsHand).sortedCardsByValue);
So I would like to achive object sortedCardsByValue:
[
{ value: '1', suit: 'C' },
{ value: '10', suit: 'C' },
{ value: '11', suit: 'C' },
{ value: '12', suit: 'C' },
{ value: '13', suit: 'C' }
]`
and object convertedCards (which is sorted like parent but with changed names for example 1->A; 11->J):
[
{ value: 'A', suit: 'C' },
{ value: '10', suit: 'C' },
{ value: 'J', suit: 'C' },
{ value: 'Q', suit: 'C' },
{ value: 'K', suit: 'C' }
]
But my code from the beginning creates both objects the same.
I have made some functions to solve this bug. I've tried map methos, replace method, forEach, Object.values().
This let convertedCards = cardsHand; means - create a new variable with a reference to exactly the same object
This let sortedCardsByValue = []; does nothing because you assign a different array in the next line
Sort works in place so you don't have to assign to a new variable
const cardsHand = [{
value: "1",
suit: "C"
},
{
value: "13",
suit: "C"
},
{
value: "12",
suit: "C"
},
{
value: "11",
suit: "C"
},
{
value: "10",
suit: "C"
},
];
function sortCards(cardsHand) {
cardsHand.sort(
(currCardA, currCardB) => currCardA.value - currCardB.value);
let convertedCards = cardsHand.map(obj => ({ ...obj
}));
convertedCards.forEach((element) => {
if (+element.value === 1) element.value = `A`;
if (+element.value === 11) element.value = `J`;
if (+element.value === 12) element.value = `Q`;
if (+element.value === 13) element.value = `K`;
});
return {
convertedCards,
sortedCardsByValue: cardsHand,
};
}
const {
convertedCards,
sortedCardsByValue,
} = sortCards(cardsHand)
console.log(convertedCards);
console.log(sortedCardsByValue);

Filter array of objects with sub arrays

I have next array:
const arr = [
{
id: 1,
food: ['cola', 'pizza pie'],
size: [{value: 12}, {value: 14}],
},
{
id: 2,
food: ['water', 'wine', 'pasta'],
size: [{value: 15}, {value: 19}],
},
{
id: 3,
food: ['water', 'wine', 'pasta'],
size: [{value: 1}, {value: 13}],
},
];
I need to filter this array in the next way:
If I have the same 'food' values, the array item which has the highest 'size->value', should left, other removed.
Expected result:
const arr = [
{
id: 1,
food: ['cola', 'pizza pie'],
size: [{value: 12}, {value: 14}],
},
{
id: 2,
food: ['water', 'wine', 'pasta'],
size: [{value: 15}, {value: 19}],
},
];
What is the best way for this?
Your requirement as a little vague - particularly when you said:
If I have the same 'food' values, the array item which has the highest 'size->value', should left, other removed.
But here is an approach using Array.prototype.reduce (assuming the comparison is between the total sum of size values when duplicates are found). I've commented the code to give an idea on what's happening.
const arr = [{
id: 1,
food: ['cola', 'pizza pie'],
size: [{
value: 12
}, {
value: 14
}],
},
{
id: 2,
food: ['water', 'wine', 'pasta'],
size: [{
value: 15
}, {
value: 19
}],
},
{
id: 3,
food: ['water', 'wine', 'pasta'],
size: [{
value: 1
}, {
value: 13
}],
},
];
function process(arr) {
return arr.reduce((acc, curr) => {
const item = acc.filter(x => curr.food.sort().toString() === x.food.sort().toString()) // check if there is another entry with the same `food` value
if (item.length) {
// comparision logic goes here
const previousSizes = item[0].size.reduce((a, b) => a + b.value, 0) // previous item's total size
const currentSizes = curr.size.reduce((a, b) => a + b.value, 0) // current item's total size
if (currentSizes > previousSizes) {
return [...acc.filter(x => x !== item[0]), curr] //remove the previous item and add the new one
} else return acc // don't change
} else return [...acc, curr] // curr is a new item.. so just add it
}, [])
}
console.log(process(arr))
The following sorts the input by food items to gather similar items together. It then walks the sorted input deleting items as it finds new maximums. It records the current maximum for each food grouping in a hashtable.
I think time complexity is n log(n) and the space complexity is n.
This implementation assumes the largest value in each size array is what you care about.
Pseudocode:
00. Create hashtable `hash`
01. Sort array `arr` by food
02. For each item `i` in `arr`
03. Let `key` be a unique key for `i` based on food
04. Let `largestSize` be the largest number in `i.size`
05. if `hash` does not contain `key`
06. Set value of `key` in `hash` to `largestSize`
07. else
08. if `largestSize` is larger than hash.key
09. replace `m` and delete previous item in `arr` because current item is larger
10. else
11. delete current item in `arr` because it is too small
12. end if
13. end if
14. end for
15. return `arr`
function filter(arr) {
const hash = {}
arr.sort(({food: a},{food: b})=>String(a).localeCompare(String(b)))
for (let x = 0; x < arr.length; x++) {
const {food, size} = arr[x]
const [{ value: largestSize }] = size.sort(({value: a},{value: b})=>a - b).slice(-1)
const key = String(food)
if (!hash[key])
hash[key] = largestSize
else {
if (largestSize > hash[key]) {
arr.splice(x - 1, 1)
hash[key] = largestSize
} else
arr.splice(x, 1)
--x
}
}
return arr
}
const arr = [
{
id: 1,
food: ['cola', 'pizza pie'],
size: [{value: 12}, {value: 14}],
},
{
id: 2,
food: ['water', 'wine', 'pasta'],
size: [{value: 15}, {value: 19}],
},
{
id: 3,
food: ['water', 'wine', 'pasta'],
size: [{value: 1}, {value: 13}],
},
]
console.log(filter(arr))

Comparing array elements against the rest of the array

The question might be a bit vague, but I'll explain the result I'm expecting to get with an example.
Say I have the following array made out of objects with the following shape:
[
{
id: 1,
value: 10
},
{
id: 2,
value: 100
},
{
id: 3,
value: 10
},
{
id: 4,
value: 10
},
{
id: 5,
value: 1000
},
]
This array might contain hundrends, maybe thousands of entries, but for simplicity, I'll keep it small.
What I'm trying to achieve is compare the value property of every object with the other value properties and assign a new property duplicate with a boolean value to that specific object.
Given the example above, I would expect to receive an array with the following members:
[
{
id: 1,
value: 10,
duplicate: true
},
{
id: 2,
value: 100
},
{
id: 3,
value: 10,
duplicate: true
},
{
id: 4,
value: 10,
duplicate: true
},
{
id: 5,
value: 1000
},
]
Whats the most optimal way I could implement this behavior ?
Thank you.
I'd do a single pass through the array remembering the first seen entry with a given value in a Map, marking that first entry (and any others) as duplicates if it's present, like this:
const map = new Map();
for (const entry of array) {
const previous = map.get(entry.value);
if (previous) {
previous.duplicate = entry.duplicate = true;
} else {
map.set(entry.value, entry);
}
}
Live Example:
const array = [
{
id: 1,
value: 10
},
{
id: 2,
value: 100
},
{
id: 3,
value: 10
},
{
id: 4,
value: 10
},
{
id: 5,
value: 1000
},
];
const map = new Map();
for (const entry of array) {
const previous = map.get(entry.value);
if (previous) {
previous.duplicate = entry.duplicate = true;
} else {
map.set(entry.value, entry);
}
}
console.log(array);
You can do this by first determining which are the duplicates, and then setting the 'duplicate' attribute.
counts = items.reduce((counter, item) => {
if (counter[item.value] != null) {
counter[item.value] += 1;
} else {
counter[item.value] = 1;
}
return counter;
}, {});
After this, you can go over your items, and if the count is >=2, set the 'duplicate' attribute.
items.forEach((item) => {
if (counter[item.value] > 1) {
item['duplicate'] = true;
}
});
You can use Array.map and Array.filter for that.
const input = [
{ id: 1, value: 10 },
{ id: 2, value: 100 },
{ id: 3, value: 10 },
{ id: 4, value: 10 },
{ id: 5, value: 1000 }
]
const output = input.map(entry => {
if (input.filter(x => x.value === entry.value).length > 1) {
return {
duplicate: true,
...entry
}
}
return entry
})
console.log(output)
I would create a map with value as the key, and a list of ids as the values, than after iterating over the whole map and creating the new mapping, unpack it back tothe desired form, and add duplicated for keys with more than one value.
I think this will help you. arr is your array.
arr.forEach(e=> {
const dublicatedDataLenth = arr.filter(a => a.value == e.value).length;
if(dublicatedDataLenth > 1){
e.dublicate = true;
}
})
It should be what you are looking for.
A copy from myself with a single loop and an object for storing seen values.
This approach returns a new array and does not mutate the given data.
var data = [{ id: 1, value: 10 }, { id: 2, value: 100 }, { id: 3, value: 10 }, { id: 4, value: 10 }, { id: 5, value: 1000 }],
result = data.map((seen => ({ ...o }) => {
if (o.value in seen) {
o.duplicate = true;
if (seen[o.value]) {
seen[o.value].duplicate = true;
seen[o.value] = false;
}
} else seen[o.value] = o;
return o;
})({}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript - How to remove all elements of an array within an array

Currently when I console.log(deckOfCards), it is returning
all 52 cards, each with a suit, value, and points assigned to them.
{ suit: '♦', value: 'A', points: 11 }
{ suit: '♦', value: 2, points: 2 }
{ suit: '♦', value: 3, points: 3 }
.....
Now, I want to remove one card that has the suit, value and points from my deckOfCards array and return that.
{ suit: '♦', value: 'A', points: 11 }
This is to simulate dealing one card from the deck.
I have tried accessing each index of the array and adding them to the card variable, but it gave me an undefined for index 2.
For loops only return one array of suits and not the others.
I have changed the deckOfCards into an object that has the suit, value, and points in it.
My card constant is where I want to pull one card from the deck.
const suits = ["♦", "♣", "♥", "♠"];
const values = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"];
for (const suit of suits) {
for (const value of values) {
let points = parseInt(value);
if(value === "J" || value === "Q" || value === "K") points = 10;
if(value === "A") points = 11;
const deckOfCards = {suit, value, points};
const card = deckOfCards
}
}
EDIT TRYING TO ADD NEW METHOD
I'm trying to add two cards each to the player/dealer hands,
but when i log it:
[ { suit: '♠', value: 'A', points: 11 } ]
[ { suit: '♠', value: 'A', points: 11 },
{ suit: '♦', value: 10, points: 10 } ]
Why am I getting 3 objects returned instead of 2?
const dealRandomCard = () => {
return deckOfCards.splice(Math.floor(Math.random() *
deckOfCards.length), 1)[0];
}
// console.log(dealRandomCard());
//////////////////////////////////////////////////////////////
for (let i = 0; i <= 1; i++) {
playerHand.push(dealRandomCard());
dealerHand.push(dealRandomCard());
console.log(playerHand);
// console.log(dealerHand);
}
You could use a single combined object to the result set. And an object for a shorter way of getting the points.
var suits = ["♦", "♣", "♥", "♠"],
values = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"],
cards = [],
suit,
value,
points = { A: 11, J: 10, Q: 10, K: 10 };
for (suit of suits) {
for (value of values) {
cards.push({ suit, value, points: points[value] || value });
}
}
function getCard() {
return cards.splice(Math.floor(Math.random() * cards.length), 1)[0];
}
console.log(getCard());
console.log(getCard());
console.log(getCard());
console.log(cards);

Replace deep child object, while keeping state immutable in React

I'm new to react and redux. I have an object is an array of child objects, which contain arrays
const initialState = {
sum: 0,
denomGroups: [
{
coins: [
{ name: 'Penny', namePlural: 'Pennies', label: '1¢', value: .01, sum: 0 },
{ name: 'Nickel', namePlural: 'Nickels', label: '5¢', value: .05, sum: 0 },
{ name: 'Dime', namePlural: 'Dimes', label: '10¢', value: .10, sum: 0 },
{ name: 'Quarter', namePlural: 'Quarters', label: '25¢', value: .25, sum: 0 }
]
},
{
bills: [
{ name: 'Dollar', namePlural: 'Dollars', label: '$1', value: 1, sum: 0 },
{ name: 'Five', namePlural: 'Fives', label: '$5', value: 5, sum: 0 },
{ name: 'Ten', namePlural: 'Tens', label: '$10', value: 10, sum: 0 },
{ name: 'Twenty', namePlural: 'Twentys', label: '$20', value: 20, sum: 0 },
{ name: 'Fifty', namePlural: 'Fiftys', label: '$50', value: 50, sum: 0 },
{ name: 'Hundred', namePlural: 'Hundreds', label: '$100', value: 100, sum: 0 }
]
}
]
};
I have an action that gets passed a value and the name of the denomination
export function increaseSum(value, denom) {
return { type: types.ADD_TO_SUM, value: value, denom: denom }
}
Inside of my reducer I wrote a helper class to identify where this denomination is within the object:
function findDenomination(denoms, action) {
let denomMap = {},
currentDenom = {};
for (let i = 0; denoms.length >= i + 1; i++) {
let denomGroup = denoms[Object.keys(denoms)[i]];
for (var key in denomGroup) {
if (!currentDenom.length) {
currentDenom = denomGroup[key].filter(x => x.name === action.denom);
if (currentDenom.length > 0) {
denomMap.group = i;
denomMap.key = key;
denomMap.index = denomGroup[key].findIndex(x => x.name === action.denom);
}
}
}
if (currentDenom.length > 0) {
break;
}
}
return denomMap;
}
And within the reducer itself I am using Object.assign to make a deep copy of the denomGroups, in a way that I would think keeps it immutable.
function countableItems(state = initialState, action) {
switch (action.type) {
case types.ADD_TO_SUM:
let denomMap = findDenomination(state.denomGroups, action);
state.denomGroups = Object.assign({}, state.denomGroups, state.denomGroups[denomMap.group][denomMap.key][denomMap.index].sum = parseFloat(action.value));
return state;
default:
return state;
}
}
Is it clear to anyone why this is getting flagged with the error: A state mutation was detected inside a dispatch
You attempted to mutate an immutable. Hence the error.
The whole point of immutables is that an immutable should never ever change anywhere inside of it after it's create. That's why all functions that change it create a new instance of an immutable. If you try to change something deep inside an immutable, that's still a mutation, and thus bad.
This ensures you can just check the objects themselves for equality instead of having to do a deep check, as well as ensuring data integrity.
What you should be doing is just mutating it (if you're using the immutables library, you can use setIn), which will create a new Map. You can call that just on state.
Something like this:
case types.ADD_TO_SUM:
const denomMap = findDenomination(state.denomGroups, action);
return state.setIn(['denomGroup', denomGroup.key, denomGroup.index, sum], parseFloat(action.value));

Categories