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?
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) });
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().
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());
Related
So I have an array like this:
items = [2, 2, 5, 3, 5, 3, 4, 7, 2, 7];
Is there a way to use items.length property here to return 5 instead of 10. I have seen a method where a function was used to get the count of the distinct elements by passing the length property as an argument. What I want to achieve is slightly different. I want the length to be printed by calling the Array.length property instead of through a function. Is it possible at all?
Here is the function:
function countDistinct(arr, n) {
let res = 1;
// Pick all elements one by one
for (let i = 1; i < n; i++) {
let j = 0;
for (j = 0; j < i; j++)
if (arr[i] === arr[j])
break;
// If not printed earlier, then print it
if (i === j)
res++;
}
return res;
}
// Driver program to test above function
let arr = [2, 2, 5, 3, 5, 3, 4, 7, 2, 7];
let n = arr.length;
console.log((countDistinct(arr, n)));
This is barely possible, but it's really weird. If you create another object that wraps the array, you can make the length property a getter that deduplicates items and returns the size, something along the lines of:
const makeSpecialArr = (arr) => {
const specialArr = Object.create(arr);
Object.defineProperty(specialArr, 'length', {
get() {
return new Set(arr).size;
},
});
return specialArr;
};
const arr = makeSpecialArr([2, 2, 5, 3, 5, 3, 4, 7, 2, 7]);
console.log(arr.length);
You can't do this without creating another object around the array because an array's .length is an own non-configurable property.
If you wanted to do something like this, consider using a property other than length, which makes it a lot easier:
const makeSpecialArr = (arr) => {
Object.defineProperty(arr, 'dedupLength', { get() {
return new Set(arr).size;
}});
return arr;
};
const arr = makeSpecialArr([2, 2, 5, 3, 5, 3, 4, 7, 2, 7]);
console.log(arr.dedupLength);
But even this is strange. Might be interesting as theoretical code, or for a thought experiment, but I wouldn't use it for anything serious that had to be maintained.
You can use reduce for this:
var count = arr.reduce(function(values, v) {
if (!values.set[v]) {
values.set[v] = 1;
values.count++;
}
return values;
}, { set: {}, count: 0 }).count;
This question already has answers here:
Reverse array in Javascript without mutating original array
(15 answers)
Closed 2 years ago.
I have been participating in some javaScript challenges and solved the reverse array challenge without modifying the original using the spread operator. I enjoy solving problems in different ways so i'm curious to find out from you. In what other way would you have solved it or would you solve it (excluding high order functions like map etc) ?
var newArray = [1,2,3,4,5,6];
const reverseArray = () => {
let arr = [...newArray];
for(let i = 0; i <= arr.length; i++){
arr.pop(i)
arr.unshift(i);
}
return arr
}
console.log(reverseArray())
You can use reverse();
var newArray = [1, 2, 3, 4, 5, 6];
var reverse = newArray.reverse();
console.log(reverse)
Use a for loop with increment to set index and then decrement value and push into array
var myArray = [1, 2, 3, 4, 5, 6, 7, 8];
function reverseArray(myArray) { // create a function and pass our array into it
var newArray = []; // define an empty array
for (var i = myArray.length - 1; i >= 0; i--) { // set for loop, declare a decrement for index as i - 1 to adjust for index, if greater than or equal to 0, decrement i
newArray.push(myArray[i]); // push the value into newArray
}
return newArray; // return newArray
}
console.log(reverseArray(myArray));
Use slice() reverse() and map() together index and value through function.
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var div = document.getElementById('div')
function sliceMap() {
reverseArray = myArray.slice(0).reverse().map(
function(value) {
return value;
}
);
div.innerHTML = reverseArray;
}
console.log(sliceMap())
<div id="div"></div>
Mapping values and then using unshift to reverse them.
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var reverseArray = [];
myArray.map((value) => {
reverseArray.unshift(value);
});
console.log(reverseArray)
New here and been trying to figure this out for a bit now. Can't seem to find the answer.
Problem: trying to separate all numbers from 5 upwards into a separate array "bigNumbers". All other numbers to "smallNumbers"
Here's what I have so far:
let allNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let bigNumbers = [];
let smallNumbers = [];
allNumbers.forEach(function (a) {
if(allNumbers >= 5) {
return allNumbers.push(bigNumbers);
} else {
return allNumbers.push(smallNumbers);
}
});
Might be taking the wrong approach entirely here using the .push() method. Any feedback is appreciated.
You're testing the wrong variable, it should be a, not allNumbers. And the argument to .push() is the value you want to push onto the array, not the array to push onto. There's also no need to use return, since forEach doesn't use the return values.
let allNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let bigNumbers = [];
let smallNumbers = [];
allNumbers.forEach(function (a) {
if(a >= 5) {
bigNumbers.push(a);
} else {
smallNumbers.push(a);
}
});
console.log("Big: " + JSON.stringify(bigNumbers));
console.log("Small: " + JSON.stringify(smallNumbers));
Trouble is in your if (allNumbers >= 5)
What you want is to know if the current number being iterated is greater than 5:
if (a >= 5)...
I want to loop through an array and update elements in place. I only want to loop through elements that fit a filter. How would I do this?
naive approach:
arr.forEach(element => {
if (element.val > 5) {
element = 9;
}
});
I expect that with .map and .filter I should be able to do this more efficiently and inline.
No need of filter(), just use Array.prototype.map() like the following:
var arr = [5, 10, 15]
arr = arr.map(element => element > 5? element = 9 : element);
console.log(arr);
Since the question asks for an "in place" update, this approach updates the existing array:
let arr = [20, 5, 6, 1];
arr.forEach((e, i) => { if (e > 5) arr[i] = 9; })
console.log(arr)
Now, the question actually also asks for an approach that only loops through the items that match a filter, but that seems superfluous, since something has to loop through every element.
If you don't care much about performance and want to have a little fun:
Array.prototype.mutate = function (cb) {
var updates = {};
this.forEach(function (element, index) {
var wrapper = {
value: function () {
return element;
},
update: function (value) {
updates[index] = value;
}
};
cb(wrapper, index);
});
var self = this;
Object.keys(updates).forEach(function (index) {
var value = updates[index];
self[index] = value;
});
};
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
a.mutate(function (wrapper) {
if (wrapper.value() % 2 === 0) {
wrapper.update(wrapper.value() * 2);
}
});
console.log(a); //[ 1, 4, 3, 8, 5, 12, 7, 16, 9, 20 ]
I would like to know how to compare two or more -- potentially unlimited -- arrays for common values and push these values into a new array efficiently. Below I have a function that will accept unlimited arguments, but I am uncertain if this is a good place to begin. PHP appears to have a method that can do what I want called array_intersect. Does javascript offer something similar?
Note: I have found examples of how this can be done with two or so arrays, but I have not found examples of how such approaches might be applied to an unspecified number of arrays as of yet. Therefore I do not see this as a duplicate question.
To further clarify, the arrays might be filled with anything. Letters, numbers, symbols, words, you name it, it might be there.
var sampleOne = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var sampleTwo = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
function FindDirectRelation() {
for(var i = 0; i < arguments.length; ++i) {
console.log(arguments[i]);
};
};
var directRelation = FindDirectRelation(sampleOne, sampleTwo);
I am still a coding novice, so please ensure that everything is explained in a way that is simple enough for me to understand.
using an existing intersect that works with 2 arrays, we can chain together a common sub-set using the built-in reduce() method on an array of arrays that need intersected:
function intersect(a, b) {
var aa = {};
a.forEach(function(v) { aa[v]=1; });
return b.filter(function(v) { return v in aa; });
}
var r1=[1,2,3],
r2=[1,3,4,5],
r3=[5,1,3];
alert([r1, r2, r3].reduce(intersect)) // shows: 1,3
if you define "intersect" as just being in more than one array (not every), then it's more complex...
Check to make sure the elements in the first array are also in the remaining arrays:
function multi_intersect(a) {
var other_arrays = Array.prototype.slice.call(arguments, 1);
return a . filter(function(elt) {
return other_arrays.every(function(an) {
return an.indexOf(elt) !== -1;
});
});
}
Try using Array.prototype.filter() , Array.prototype.indexOf()
var res = sampleOne.filter(function(val) {return sampleTwo.indexOf(val) !== -1})
var sampleOne = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var sampleTwo = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
var arr = ["a", "b", "c"];
var arr1 = ["c", "d", "e"];
var arr2 = [2, 7];
function samples() {
var args = Array.prototype.slice.call(arguments);
var res = [];
for (var i = 0, curr, next; i < args.length; i++) {
if (args[i + 1]) {
// set `curr` to array `i`
curr = args[i];
// set `next` to array `i + 1` if it exists
next = args[i + 1]
} else {
// if at last index, set `curr` to `args` : input arrays
// flattened to single array , with element at `i` removed
curr = [].concat.apply([], args.slice(0, args.length - 1));
console.log(curr)
// set next to current index
next = args[i];
};
next = next.filter(function(val) {
return curr.indexOf(val) !== -1
// filter duplicate entries at `res`
&& res.indexOf(val) === -1
});
res = res.concat.apply(res, next);
};
return res
}
var sample = samples(sampleOne, sampleTwo, arr, arr1, arr2);
console.log(sample); // [5, 6, 7, 8, 9, 10, 11, 12, "c", 2]