Change values in object without changing parent object - javascript

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

Related

Determine if a function has two pairs

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
}

Create nested object from serializeArray

I'd like to create this structure:
{
"officine_type": "Pharmacie",
"officine_value": 2002626,
"declared_lines": [
{
"ean": 3578835501148,
"qty": 1
},
{
"ean": 3578835502671,
"qty": 2
}
],
"other_value": "my other value"
}
From a serializeArray() with this output:
0: {name: 'declared_lines.0.ean', value: '3578835502244'}
1: {name: 'declared_lines.0.qty', value: '4'}
2: {name: 'declared_lines.1.ean', value: '3578835502220'}
3: {name: 'declared_lines.1.qty', value: '1'}
4: {name: 'declared_lines.2.ean', value: ''}
5: {name: 'declared_lines.2.qty', value: '0'}
6: {name: 'officine_type', value: 'Pharmacy'}
7: {name: 'officine_value', value: '2000461'}
8: {name: 'other_value', value: ''}
I'm struggling on how to push sub-objects in declared_lines
Right now i have this:
let formData = form.serializeArray();
for (let i = 0; i < formData.length; i++) {
if (formData[i]['name'].indexOf('declared_lines') !== 1) {
let inputName = formData[i]['name'].split('.');
let namespace = inputName[0];
let n = inputName[1];
let key = inputName[2];
let subObj = {};
let current = 'declared_lines['+i+']';
let previous = 'declared_lines['+(i-1)+']';
if (obj.hasOwnProperty(namespace) === false) {
obj[namespace] = [];
}
}
obj[formData[i]['name']] = formData[i]['value'];
}
My brain won't go further :(
You could take the name and split it by dot for the path of the new object and the value and build a new object with the given information.
In setValue, the reduce callback checks if the next key is a stringed numerical value and takes an array as default object instead of an object.
function setValue(object, path, value) {
const last = path.pop();
path
.reduce((o, k, i, kk) => o[k] ??= (isFinite(i + 1 in kk ? kk[i + 1] : last) ? [] : {}), object)
[last] = value;
return object;
}
const
data = [{ name: 'declared_lines.0.ean', value: '3578835502244' }, { name: 'declared_lines.0.qty', value: '4' }, { name: 'declared_lines.1.ean', value: '3578835502220' }, { name: 'declared_lines.1.qty', value: '1' }, { name: 'declared_lines.2.ean', value: '' }, { name: 'declared_lines.2.qty', value: '0' }, { name: 'officine_type', value: 'Pharmacy' }, { name: 'officine_value', value: '2000461' }, { name: 'other_value', value: '' }],
result = data.reduce(
(object, { name, value }) => setValue(object, name.split('.'), value),
{}
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Object destructuring and array.reduce can make your code more readable, try:
let formData = [
{name: 'declared_lines.0.ean', value: '3578835502244'},
{name: 'declared_lines.0.qty', value: '4'},
{name: 'declared_lines.1.ean', value: '3578835502220'},
{name: 'declared_lines.1.qty', value: '1'},
{name: 'declared_lines.2.ean', value: ''},
{name: 'declared_lines.2.qty', value: '0'},
{name: 'officine_type', value: 'Pharmacy'},
{name: 'officine_value', value: '2000461'},
{name: 'other_value', value: ''}
];
let output = formData.reduce((acc,cur) => {
let { name, value } = cur;
if(name.indexOf('declared_lines') === -1){
acc[name] = value;
} else {
let [namespace, n, key] = name.split('.');
if(!acc[namespace]) acc[namespace] = [];
if(!acc[namespace][n]) acc[namespace][n] = {};
acc[namespace][n][key] = value;
}
return acc;
}, {});
console.log(output);
In this case reduce starts with an empty object and it loops over your array to process each element (cur).

Convert a key-value array with duplicate keys into array of object with unique key, and value array property

I have an array of key/value pairs. The keys are sometimes duplicated, and the values are always unique per key.
I want to condense each unique key to an object, so that I have a key and an array of the associated values as a property.
Are there any handy javascript functions to do this?
This
pairArray = [
{ key: "a", value: "1" },
{ key: "a", value: "2" },
{ key: "b", value: "1" },
{ key: "b", value: "2" },
];
Becomes
objectArray = [
{ key: "a", values: ["1", "2"] },
{ key: "(", values: ["1", "2"] }
];
You can simply create a map using Array.reduce() with your key property of your object as key of your map, Object.values() on that map will give you the desired result :
Assuming you have a typo in your expected output. You can try the following :
const pairArray = [ { key: "a", value: "1" }, { key: "a", value: "2" }, { key: "b", value: "1" }, { key: "b", value: "2" }, ];
const result = Object.values(pairArray.reduce((acc, {key, value})=>{
acc[key] = acc[key] || {key, values : []};
acc[key].values.push(value);
return acc;
},{}));
console.log(result);
You can use Map() to get the desired output:
let data = [
{ key: "a", value: "1" },
{ key: "a", value: "2" },
{ key: "b", value: "1" },
{ key: "b", value: "2" },
];
let reducer = arr => {
let map = new Map();
arr.forEach(({key:k, value:v}) => {
let values = map.get(k) || [];
values.push(v);
map.set(k, values);
});
return [...map.entries()].map(([k, v]) => ({key: k, values: v}));
};
console.log(reducer(data));

How to sort an array from an object?

How to sort an array from an object ?
The code :
let A = [ { text: '故事', value: 'story', },
{ text: '诗歌', value: 'poetry', },
{ text: '励志', value: 'inspirational', }
];
// array B from backend**
let B = {
story: 2,
poetry: 34,
inspirational: 30,
};
I want to get this :
[
{ text: '诗歌', value: 'poetry', },
{ text: '励志', value: 'inspirational'},
{ text: '故事', value: 'story', },
];
Simply you can use JavaScript sort function.
Note: When sorting numbers, you can simply use the compact comparison:
Compact Comparison:: myArray.sort((n1,n2) => n1 - n2);
let A = [ { text: '故事', value: 'story', },
{ text: '诗歌', value: 'poetry', },
{ text: '励志', value: 'inspirational', }
];
// array B from backend**
let B = {
story: 2,
poetry: 34,
inspirational: 30,
};
A.sort((a, b) => B[b.value]-B[a.value] );
console.log(A);
You can use an arrow function in array.sort as a custom comparator. Sorting in reverse order is accomplished by indexing into the B object to retrieve the sort value for compared elements and subtracting a's value from b.
let A = [
{ text: '故事', value: 'story', },
{ text: '诗歌', value: 'poetry', },
{ text: '励志', value: 'inspirational', }
];
let B = {
story: 2,
poetry: 34,
inspirational: 30,
};
const sorted = A.sort((a, b) => B[b.value] - B[a.value]);
console.log(sorted);
You can use sort() to arrange the array elements. You can use Number.NEGATIVE_INFINITY as a default value if the value does not exist on B. This will put the undefined value last.
let A = [{"text":"诗歌","value":"poetry"},{"text":"励志","value":"inspirational"},{"text":"故事","value":"story"}];
let B = {"story":2,"poetry":34,"inspirational":30};
A.sort((x, y) => (B[y.value] || Number.NEGATIVE_INFINITY) - (B[x.value] || Number.NEGATIVE_INFINITY));
console.log(A);
Try this, it uses an arrow function and array.sort:
let A = [{
text: '故事',
value: 'story',
},
{
text: '诗歌',
value: 'poetry',
},
{
text: '励志',
value: 'inspirational',
}
];
// array B from backend**
let B = {
story: 2,
poetry: 34,
inspirational: 30,
};
A.sort((a, b) => B[b.value] - B[a.value]);
console.log(A);
Using the function Array.prototype.sort you can accomplish your desired output.
This B[bV] - B[aV] will return a value lesser than 0 or greater than 0 or equal to 0 which is what the function sort is expecting on to locate the elements at the specific index according to their value from object B.
let A = [{ text: '故事', value: 'story', }, { text: '诗歌', value: 'poetry', }, { text: '励志', value: 'inspirational', }],
B = { story: 2, poetry: 34, inspirational: 30};
A.sort(({value: aV}, {value: bV}) => B[bV] - B[aV]);
console.log(A);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Filter object array against another object array

I have two arrays simplified like this.
var a = [{
number: 1,
name: "A"
}, {
number: 2,
name: "B"
}, {
number: 3,
name: "C"
}, {
number: 4,
name: "D"
}, {
number: 5,
name: "E"
}, {
number: 6,
name: "F"
}];
var b = [{
number: 3,
name: "C"
}, {
number: 6,
name: "F"
}];
What I want is to return an array where a is filtered against b. So the result is this.
var result = [{
number: 1,
name: "A"
}, {
number: 2,
name: "B"
}, {
number: 4,
name: "D"
}, {
number: 5,
name: "E"
}];
Have looked at alot of other solutions here on stack overflow but can't get it to work.
I am ok with using libaries like underscore.
It is possible to achieve with lodash by the one-line solution.
var a = [{
number: 1,
name: "A"
}, {
number: 2,
name: "B"
}, {
number: 3,
name: "C"
}, {
number: 4,
name: "D"
}, {
number: 5,
name: "E"
}, {
number: 6,
name: "F"
}];
var b = [{
number: 3,
name: "C"
}, {
number: 6,
name: "F"
}];
var result = _.differenceWith(a, b, _.isEqual);
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
var a = [{
number: 1,
name: "A"
}, {
number: 2,
name: "B"
}, {
number: 3,
name: "C"
}, {
number: 4,
name: "D"
}, {
number: 5,
name: "E"
}, {
number: 6,
name: "F"
}];
var b = [{
number: 3,
name: "C"
}, {
number: 6,
name: "F"
}];
var _ = require('lodash');
var result = _.differenceBy(a,b,'name');
For more informaiton please refer Lodash documentation: https://lodash.com/docs/#differenceBy
You can use Array#filter. Something like that:
const filterData = (a,b) => {
return a.filter( (itemA) => {
return b.some( (itemB) => {
return (itemA.number === itemB.number);
});
});
}
I just created a fiddle to test the code: https://jsfiddle.net/mrlew/pb1qqeyd/3/ (you'll have to open console to check results).
Or... in one-line:
const filterData = (a,b) => a.filter( (itemA) => b.some( (itemB) => (itemA.number === itemB.number) ) );
EDIT: thanks to #kzh suggestions, edited to use .some.
You can use the in-built filter function in JavaScript to filter an array with another array check the below code snippet.
var a = [{
number: 1,
name: "A"
}, {
number: 2,
name: "B"
}, {
number: 3,
name: "C"
}, {
number: 4,
name: "D"
}, {
number: 5,
name: "E"
}, {
number: 6,
name: "F"
}];
var b = [{
number: 3,
name: "C"
}, {
number: 6,
name: "F"
}];
var result = a.filter(function(currentValue, index, arr) {
var found = false;
for (var i = 0; i < b.length; i++) {
if (currentValue.number === b[i].number) {
found = true;
break;
}
}
if (!found) {
return currentValue;
}
});
console.log(result);
You can do this with plain javascript using filter(), some() and every().
var a = [{"number":1,"name":"A"},{"number":2,"name":"B"},{"number":3,"name":"C"},{"number":4,"name":"D"},{"number":5,"name":"E"},{"number":6,"name":"F"}]
var b = [{"number":3,"name":"C"},{"number":6,"name":"F"}]
var result = a.filter(function(o) {
return !b.some(function(e) {
return Object.keys(o).length == Object.keys(e).length && Object.keys(o).every(function(k) {
return e[k] == o[k]
})
})
})
console.log(result)
var v1 = JSON.parse(a);
var v2 = JSON.parse(b);
var v3 = [] ;
function objectEquals(v1, v2) {
if (typeof(v1) !== typeof(v2)) {
return false;
}
if (v1 instanceof Object && v2 instanceof Object) {
for (k in v1) {
r = objectEquals(v1[k], v2[k]);
if (!r) {
v3.push(v1[k]);
}
}
}
}
// call the above method passing your two object, and return a new unique array
objectEquals(v1, v2) ;

Categories