I am trying to create a deck of 52 cards. I can create it easily with double for-loop but it has O(n2) complexity. So I was trying to play with map() and forEach() array methods but things are complex with them needing to return stuffs. Here is my code below.
(function deckCreate() {
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var suits = ["clubs", "diamonds", "hearts", "spades"];
var newDeck = values.map(function(xValue) {
suits.forEach(function(xSuit) {
return [xSuit,xValue];
});
});
return newDeck;
}());
It gives an array of length 13 all undefined inside. I tried swapping forEach() before map() just incase but the result was the same.
The issue I found while console.log() inside those functions was that the elements were not being mapped to each other but were printed all separately. What could be the issue be?
4 Answers 4
You're not returning anything from your map function, so the implicit return value is undefined, hence your array of 13 undefined values.
suits.forEach needs to be return suits.map. This will give you an array of 13 elements, where each element is an array of four elements, where each element of the inner array is a two element [suit, value] array. You can then reduce the top-level array into the 52 element array you're after:
var newDeck = values.map(function(xValue) {
return suits.map(function(xSuit) {
return [xSuit,xValue];
});
}).reduce(function (a, b) { return a.concat(b) });
10 Comments
.reduce() call?map(map(...)) is a a nested array of 13(4(2)) elements. The question was how to produce a 52 element array of suit/number pairs. Reduce concatenates the 13*4 arrays into a single 52 element array. You could also pass the top-level array to Array.prototype.concat.apply and achieve the same results.The reason you're having trouble is that you aren't returning from your outer .map() callback. Even if you did, though, [].forEach always returns undefined, regardless of what happens inside its callback.
So, since you're using forEach to iterate the inner array, you get an array of 13 undefineds from map.
What you should be using is .map() all the way down and return every step of the way:
const first = [1, 2, 3];
const second = ['a', 'b', 'c'];
const mapped = first.map(function(digit) {
return second.map(function(letter) {
return [digit, letter];
});
});
console.log(mapped);
Seeing how you're clearly trying to learn and improve yourself, I'll leave you to adjust this example to your specific case.
P.S. If you want a flattened array, have a look at [].reduce() and [].concat().
2 Comments
return [xSuit,xValue]" Given pattern at Question should first and second be reversed at .map() calls?Use a simple for loop. Use suits[Math.floor(i / 13)] to get the right suit, and use the remainder operator % to get the card number for each suit:
function deckCreate() {
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var suits = ["clubs", "diamonds", "hearts", "spades"];
var newDeck = [];
for (var i = 0; i < 52; i++) {
newDeck.push([suits[Math.floor(i / 13)], values[i % 13]]);
}
return newDeck;
}
var result = deckCreate();
console.log(result);
I think it's better to simplify it.
We know that there are just 4 suits, so it's enough to get list by suit name:
function createDeck() {
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var deck = {"clubs": values.slice(), "diamonds": values.slice(), "hearts": values.slice(), "spades": values.slice()};
return deck;
}
var deck = createDeck();
console.log('CLUBS:', deck.clubs);
console.log('DIAMONDS:', deck.diamonds);
console.log('HEARTS:', deck.hearts);
console.log('SPADES:', deck.spades);
P.S. in my case I'll create a class that makes generation, iteration and etc stuff to easily use it.
function Deck() {
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var suits = ['clubs', 'diamonds', 'spades', 'hearts'];
this.getSuits = function() {return suits.splice();}
this.getValues = function() {return values.splice();}
var asObject;
this.asObject = function() {
if(asObject) return asObject;
asObject = {};
suits.map(function(suit) {
asObject[suit] = values.slice();
});
return asObject;
};
var asArray;
this.asArray = function() {
if(asArray) return asArray;
asArray = [];
suits.map(function(suit) {
values.map(function(value) {
asArray.push([suit, value]);
});
});
return asArray;
}
this.iterate = function(fn) {
this.asArray().map(fn);
}
}
var deck = new Deck();
deck.iterate(function(card) {
console.log('CARD: ', card[0], card[1]);
});
console.log(deck.asObject());
console.log(deck.asArray());
3 Comments
newDeck accessed outside of IIFE?
undefinedbecause.forEachdoesn't return anything. Change that to.mapand put areturnat the beginning of that line and it should work correctly. As far as performance I'd be surprised if this took more than 1ms.