Iterate through An Array To Create New Class (JS) - javascript

I'm working on using Classes with JavaScript and am not sure how to move forward. I think what I have below performs the for loop successfully, but I'm stuck on how to console.log the newly created Resources.
My goal is to create cards for them using Bootstrap or Foundation then appending them to the body.
My code below:
var ResourcesArray = ["Blacksmith", "Farm", "Gold", "Herb", "Quarry", "Vineyard", "Wood"]
class Resource {
constructor(name, amount) {
this.name = name;
this.amount = amount
}
add() {
this.amount + 1
}
subtract() {
this.amount - 1
}
trade() {
console.log(this.name)
}
}
function createResourceCards() {
for (i = 0; i < ResourcesArray.length; i++) {
new Resource(ResourcesArray[i], 0)
}
}
Thank you for your help!

The expression new Foo() returns the instance that was just created. Just store it in a variable:
function createResourceCards() {
for (i = 0; i < ResourcesArray.length; i++) {
let res = new Resource(ResourcesArray[i], 0);
console.log(res);
}
}
However you probably want the function to return the new resource cards. For this purpose you should make an array and push them all into the array as suggested in comments on the question. However there is also a shortcut to do this: Array#map(), demonstrated below.
const ResourcesArray = ["Blacksmith", "Farm", "Gold", "Herb", "Quarry", "Vineyard", "Wood"]
class Resource {
constructor(name, amount) {
this.name = name;
this.amount = amount
}
add() {
this.amount + 1
}
subtract() {
this.amount - 1
}
trade() {
console.log(this.name)
}
}
function createResourceCards() {
return ResourcesArray.map(res => new Resource(res, 0));
}
let resources = createResourceCards();
console.log(resources);

Each time you use new Resource() it returns the newly created class so you can either store them in an array, object or map. Object/Map with the name as the key would likely be most useful
let resourceClasses = new Map()
function createResourceCards() {
for (i = 0; i < ResourcesArray.length; i++) {
let newResource = new Resource(ResourcesArray[i], 0)
resourceClasses.set(ResourcesArray[i], newResource);
}
}
Then with this newly created Map you can get the resource classes by their name
resourceClasses.has("Blacksmith");
// Returns true/false if you have a key by this name
resourceClasses.get("Blacksmith");
// This returns the value stored in Blacksmith which is your new Class
EDIT: As #Klaycon mentions you can also use .map() as a useful way to iterate over an array and create a new array based of running logic for each element

Related

Indexed getter in Javascript

I'm using straight Javascript (no JQuery or anything like that, please). I've implemented a class which wraps an array, thus:
class Ctrls
{
_items = new Array();
constructor()
{
this._items = new Array();
}
Add(oCtrl)
{
this._items.push( { key:oCtrl.Name, value:oCtrl } );
}
Clear()
{
this._items = new Array();
}
get Count()
{
return this._items.length;
}
get Item(index)
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
get Items()
{
return this._items; // in case we desperately need to
}
}
I get an error on page load, at get Item(index), which is Uncaught SyntaxError: Getter must not have any formal parameters. I come from C# world and am looking for an equivalent of:
public Ctrl Item(iIndex)
{
get
{
return _items[iIndex];
}
}
How do I index a getter in Javascript?
Edit(1): I've had suggestions to turn get Item into a function, but if I change the definition to this:
function GetItem(index) // returns Ctrl
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
I get this error on pageload: Uncaught SyntaxError: Unexpected identifier at the line function GetItem...
Edit(2): Modified the above to read:
GetItem(index) // returns Ctrl
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
as functions within classes do not use the function keyword, oddly. This now works. Thank all.
"you can't pass parameters to getters in JS". Theoretically: yes, you cannot do that. But practically: functions are first-class citizens in JS, so they can be arguments or return values of a function. You can do it like this:
class GetterWithParameter {
constructor() {
this.array = ["index 0", "index 1", "index 2"]
}
get itemAtIndex() {
return (idx) => this.array[idx]
}
}
const getterWithParameter = new GetterWithParameter()
const idx0 = getterWithParameter.itemAtIndex(0)
const idx1 = getterWithParameter.itemAtIndex(1)
const idx2 = getterWithParameter.itemAtIndex(2)
console.log("item at index 0:", idx0)
console.log("item at index 1:", idx1)
console.log("item at index 2:", idx2)
So, while the getter cannot have arguments, you can return a function that can receive an argument - and use that.
Of course, the usage seems identical to defining a function on the class that requires the same argument - but still, you are using a getter.

Using function of an object after grabbing it from array

When I try to grab the object from the array, the type is undefined. Therefore I cannot use a method from the undefined object as it doesn't exist. I am relatively new to JavaScript and I have come straight from Java so the way of retrieving objects is kind of new to me. This is what I currently have.
var fleetAmount = 0;
var fleets = [];
function Fleet(number) {
this.number = number;
this.activities = [];
this.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(this);
}
var getFleet = function(fleetNumber) {
return fleets[fleetAmount - fleetNumber];
}
This is where I try to grab the object and preform the function
const Fl = require(‘fleet.js’);
const fleet = Fl.getFleet(fleetNumber);
fleet.addActivity(activity);
I am also working in Node.js, which is how I am using the require method.
In combination with the answer from #audzzy I changed the getFleet() function so that it would be more efficient. I tested it out and it worked. This is what I used
function getFleet(fleetNumber) {
let result = fleets.filter(function (e) {
return e.number = fleetNumber;
})
return result[0];
}
Thanks for the help! I appreciate it.
you want to create a new fleet object and add it, not "this"..
adding "this" would cause a circular reference, where
this.fleets[i] = this (and all fleets would have the same value)
when calling get fleet, I would check that a fleet was returned from get fleet
in case amount is less than the number you send to getFleet (where according to what you posted: 1 returns the last, 2 returns second to last etc..)..
I hope this explanation makes sense.. anyways, this should work..
var fleets = [];
doStuff();
function doStuff(){
addFleet(1);
addFleet(2);
addFleet(7);
addFleet(3);
// should return null
let fleet1 = getFleetByNumber(5);
// should return the fleet with number 7, and not change the fleet with number 1
let fleet2 = getFleetByNumber(7);
if(fleet2){
fleet2.addActivity("activity");
}
console.log(`fleets: ${JSON.stringify(fleets)} \nfleet1: ${JSON.stringify(fleet1)} \nfleet2: ${JSON.stringify(fleet2)}`);
}
function addFleet(number) {
let fleet = { number: number,
activities: [] };
fleet.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(fleet);
}
function getFleetByNumber(fleetNumber) {
return fleets.find(function (e) {
return e.number == fleetNumber;
});
}
function getFleet(fleetNumber) {
let result = null;
if(fleets.length - fleetNumber >= 0){
result = fleets[fleets.length - fleetNumber];
}
return result;
}

Sort method for an objects property that is inside an array of objects from a class

I need help with my sort() method from inside the AddressBook class. I tried to figure it out on my own from examples on stackoverflow but I can't seem to get it to work since most of the examples don't involve working from a class instance. If you could please look at the sort() method and let me know where I am going wrong. I think i need to loop through somehow and then reposition the array order.
window.onload = init;
let abm;
function init() {
abm = new AddressBook();
}
class Contact {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
//DO NOT MODIFY ABOVE THIS LINE
function formSubmitted() {
event.preventDefault();
var user = document.getElementById("name").value;
var mail = document.getElementById("email").value;
var newContact = new Contact(user, mail);
abm.add(newContact);
abm.display();
}
function sortList() {
//CODE HERE ONLY
abm.sort();
abm.display();
}
class AddressBook {
constructor() {
this.contactList = [];
}
add(contact) {
//CODE HERE ONLY
this.contactList.push(contact);
}
display(htmlId) {
//CODE HERE ONLY
var html = "<table border='1|1'>";
for (var i = 0; i < this.contactList.length; i++){
html+="<tr>";
html+="<td>"+this.contactList[i].name+"</td>";
html+="<td>"+this.contactList[i].email+"</td>";
html+="</tr>";
}
html+="</table>";
document.getElementById("contacts").innerHTML = html;
}
sort() {
//CODE HERE ONLY
for (var i = 0; i < this.contactList.length; i++){
var tA = this.contactList[i].name.toUpperCase();
var tB = this.contactList[i].name.toUpperCase();
if (tA < tB) {
return -1;
}
if (tA > tB) {
return 1;
}
return 0;
}
}
}
this.contactList.sort((a, b) => a.name.toUpperCase() - b.name.toUpperCase());
You can learn more at Mozilla Developers
I assume you want to sort this.contactList in-place.
Note that you do not perform any assignment to this.contactList in your sort() code. This is the first bug.
The second bug is that you return a value from the function immediately, instead of sorting your data.
The third bug is, that you cannot sort in O(N) complexity (i.e. with a single pass on the data).
You need to decide which sorting algorithm you want to implement, or use the the native javascript implementation which is MergeSort.
In this case, you'd need to pass a function to express how and using which properties you want to sort your data, which is kind of what you tried to do, using -1, 1, and 0.
In this case, you can implement sort() in the following way:
sort() {
this.contactList = this.contactList.sort(function(a, b) {
var tA = this.contactList[i].name.toUpperCase();
var tB = this.contactList[i].name.toUpperCase();
if (tA < tB) {
return -1;
}
else if (tA > tB) {
return 1;
}
return 0;
}
}
Or in an equivalent way (make sure you understand why it's equivalent):
sort() {
this.contactList = this.contactList.sort(function(a, b) {
return a.name.toUpperCase() - b.name.toUpperCase();
}
}

Dynamically naming an object in Javascript

I am trying to create a Blackjack game and am having an issue when it comes to splitting the hands. Ultimately I want to create an associative array that has a total and a status for each split, treating each one as it's own hand object. One of the issues I'm having is with the naming functionality which I am trying to do dynamically so each time the Split Function is called, it creates the name according to the number of times the hand has been split.
A bit of background on what I have created so far; A card object holds the name, suit and value (ie. Queen, Clubs, 10). I have an array called cardsInDeck that hold all the card objects in a deck which is shuffled so each card can be pulled randomly. When a card is pulled from the deck, the value is pushed into an array for calculating the value, and the name and suit are concatenated and added to a string to populate the HTML to show the cards on the page.
function drawOne() {
let card = cardsInDeck.pop();
return card;
}
var handNumber = 0;
var playerHand01 = [];
var p1, p2, d1;
function initialDealOut() {
++handNumber;
p1 = drawOne();
playerHand01.push(p1.value);
++playerCount;
let p1n = p1.name + p1.suit;
let p1c = "images/" + p1n + ".png";
let p1Image = document.getElementById('player01');
p1Image.src = p1c;
}
This is repeated for the dealers first card (d1) and the Player's second card (p2) and works well and good. What I would like to do is have the playerHand01 be part of a Hand object that is in a Player array that holds all the hands, and associate a status ("action", "stand", "bust") to each Hand object.
I am trying to accomplish this with something like the code below:
var player = new Array();
function Hand(total, status) {
this.total = total;
this.status = status;
}
function Split() {
++handNumber;
let handName = "playerHand0" + handNumber;
let handTotal = "playerHandTotal0" + handNumber;
handName = new Hand {
handTotal: 0,
status: "action"
}
}
I am new to programming and I know it can be done, just know that the way I am approaching it isn't quite right.
Any recommendations would be appreciated.
I would probably just use an array to store the hands. They'll be accessible by their index. If you want to give them a name, that could be part of the hand object.
let playerHands = [];
function Split() {
++handNumber;
let newHand = new Hand ({
handName: 'playerHand' + handNumber
handTotal: 0,
status: 'action'
});
playerHands.push(newHand);
}
Alternatively, if you want to use an object (perhaps because i've misunderstood your requirements), it is possible to give an object a property dynamically. There are two different syntaxes that can be used for object properties. Both of the following have the same result:
let myObject = {};
myObject.test = 'hi';
and
let myObject = {};
myObject['test'] = 'hi';
You'll usually use the former, but if you don't know the name until runtime, the latter gives you increased flexibility, because you can access using any string. So for example, the following is perfectly valid:
let player = {};
let handName = 'playerHand0' + handnumber;
player[handName] = new Hand();
This is a little confusing to read and I don't think you've fully thought through what information you need to store.
Since the focus of this question is really how to split a hand IMO, I will show you that in hopes that it will point you in the right direction.
First the hand object. I will use es6, but you can do something similar using the constructor method that you have there.
class Hand {
constructor(total, status){
this.total = total;
this.status = action;
this.cards = [];
}
drawCard(){
this.addCard(drawOne());
}
addCard(card){
this.cards.push(card);
this.updateScore();
}
updateScore(){
// loop through and update the score and status
}
// this is the one you've been waiting for
split(player){
// can only split in certain conditions
if(this.cards.length !== 2 || this.cards[0] !== this.cards[1]){
console.log('cannot split this (?)');
return null;
}
// create the new hand
let additionalHand = new Hand(0, 'active');
additionalHand.addCard(this.cards.pop());
player.hands.push(additionalHand);
// need to draw a new card for each hand
this.drawCard();
additionalHand.drawCard()
//return the new hand in case you want to do something with that.
return additionalHand;
}
}
This may not be exactly what you want, but it should be a good initial guide to help.
I would suggest classes for each of the concepts (Player, Card, Deck, Hand, Game), and at the Game level you would keep an array of active hands, which (after a split), could have a different number than the number of players.
Here is how it could look:
class Card {
constructor(name, suit, value) {
this.name = name+'';
this.suit = suit;
this.value = typeof name === 'number' ? name
: 10 + (name === 'Ace');
}
}
class Player {
constructor(name) {
this.name = name;
}
}
class Hand {
constructor(player, total, status) {
this.player = player; // a back reference to the owning player
this.total = total;
this.status = status;
}
addCard(card) {
this.total += card.value;
}
copy() {
return new Hand(this.player, this.total, this.status);
}
}
class Deck {
constructor() {
this.cards = [];
for (let suit of ['Clubs', 'Hearts', 'Spades', 'Diamonds']) {
for (let name of ['Ace',2,3,4,5,6,7,8,9,10,'Jack','Queen','King']) {
this.cards.push(new Card(name, suit));
}
}
}
shuffle() {
for (let i = this.cards.length; i; i--) {
let j = Math.floor(Math.random() * i);
[this.cards[i - 1], this.cards[j]] = [this.cards[j], this.cards[i - 1]];
}
return this;
}
drawOne() {
return this.cards.pop();
}
}
class Game {
constructor() {
this.players = [];
this.activeHands = [];
this.currentHandId = 0;
this.deck = new Deck().shuffle();
}
addPlayer(player) {
this.players.push(player);
// Create a hand for this player:
this.activeHands.push(new Hand(player, 0, "active"));
}
deal() {
// Draw card and assign to current hand
this.activeHands[this.currentHandId].addCard(this.deck.drawOne());
}
nextHand() {
this.currentHandId = (this.currentHandId + 1) % this.activeHands.length;
}
split() {
// Insert the new, copied hand just after the current one:
this.activeHands.splice(this.currentHandId+1, 0,
this.activeHands[this.currentHandId].copy());
}
bust() {
// remove hand from active hands:
this.activeHands[this.currentHandId].status = "busted";
this.activeHands.splice(this.currentHandId, 1);
}
}
// Start a game
var game = new Game();
game.addPlayer(new Player('Helen'));
game.addPlayer(new Player('John'));
// Deal a card to the first active hand
game.deal();
game.nextHand(); // move to second player
game.deal();
// ...etc.
// When you need to split the current hand
game.split();
// Then deal a card to both hands:
game.deal();
game.nextHand(); // move to second hand of same player
game.deal();
// ..etc.
You would of course need to add more game logic and rules. But this could be a template to extend to your needs.

Pointers and array class in javascript [duplicate]

This question already has an answer here:
Double-Queue Code needs to be reduced
(1 answer)
Closed 9 years ago.
Is there any way for me to shorten this code by using pointers?
I need to make a class that has mostly the same function as a given array class unshift,shift,push and pop but with different names.
var makeDeque = function()
{
var a= [], r=new Array(a);
length = r.length=0;
pushHead=function(v)
{
r.unshift(v);
}
popHead=function()
{
return r.shift();
}
pushTail=function(v)
{
r.push(v);
}
popTail=function()
{
return r.pop();
}
isEmpty=function()
{
return r.length===0;
}
return this;
};
(function() {
var dq = makeDeque();
dq.pushTail(4);
dq.pushHead(3);
dq.pushHead(2);
dq.pushHead("one");
dq.pushTail("five");
print("length " + dq.length + "last item: " + dq.popTail());
while (!dq.isEmpty())
print(dq.popHead());
})();
Output should be
length 5last item: five
one
2
3
4
Thanks!
Maybe I'm oversimplifying, but why not just add the extra methods you need to the Array prototype and call it directly?
I need to make a class that has mostly the same function as a given array class unshift,shift,push and pop but with different names.
I suppose you could add these "new" methods to Array.prototype.
Like this perhaps?
var makeDeque = (function (ap) {
var Deque = {
length: 0,
pushHead: ap.unshift,
popHead: ap.shift,
pushTail: ap.push,
popTail: ap.pop,
isEmpty: function () {
return !this.length;
}
};
return function () {
return Object.create(Deque);
};
})(Array.prototype);
DEMO
If it's still too long, you can always directly augment Array.prototype like others already mentionned. We agree that it's all experimental here and the only goal is to save characters.
!function (ap) {
ap.pushHead = ap.unshift;
ap.popHead = ap.shift;
ap.pushTail = ap.push;
ap.popTail = ap.pop;
ap.isEmpty = function () {
return !this.length;
};
}(Array.prototype);
function makeDeque() {
return [];
}
This can be compressed to 174 chars:
function makeDeque(){return[]}!function(e){e.pushHead=e.unshift;e.popHead=e.shift;e.pushTail=e.push;e.popTail=e.pop;e.isEmpty=function(){return!this.length}}(Array.prototype)
DEMO
Not sure why you need this, but my suggestions per best practice are:
Don't override the Array.prototype. The reason for this is because other libraries might try to do the same, and if you include these libraries into yours, there will be conflicts.
This code is not needed. var a= [], r=new Array(a);. You only need ...a = [];.
Ensure you are creating a real class. In your code, makeDeque is not doing what you want. It is returning this which when a function is not called with the new keyword will be the same as the window object (or undefined if you are using what is called as "strict mode"). In other words, you have made a lot of globals (which are usually a no-no, as they can conflict with other code too).
When you build a class, it is good to add to the prototype of your custom class. This is because the methods will only be built into memory one time and will be shared by all such objects.
So I would first refactor into something like this:
var makeDeque = (function() { // We don't need this wrapper in this case, as we don't have static properties, but I've kept it here since we do want to encapsulate variables in my example below this one (and sometimes you do need static properties).
function makeDeque () {
if (!(this instanceof makeDeque)) { // This block allows you to call makeDeque without using the "new" keyword (we will do it for the person using makeDeque)
return new makeDeque();
}
this.r = [];
this.length = 0;
}
makeDeque.prototype.setLength = function () {
return this.length = this.r.length;
};
makeDeque.prototype.pushHead=function(v) {
this.r.unshift(v);
this.setLength();
};
makeDeque.prototype.popHead=function() {
return this.r.shift();
this.setLength();
};
makeDeque.prototype.pushTail=function(v){
this.r.push(v);
this.setLength();
};
makeDeque.prototype.popTail=function() {
return this.r.pop();
this.setLength();
};
makeDeque.prototype.isEmpty=function() {
return this.r.length === 0;
};
return makeDeque;
}());
Now you could shorten this as follows, but I wouldn't recommend doing this, since, as it was well said by Donald Knuth, "premature optimization is the root of all evil". If you try to shorten your code, it may make it inflexible.
var makeDeque = (function() {
function makeDeque () {
if (!(this instanceof makeDeque)) {
return new makeDeque();
}
this.r = [];
this.length = 0;
}
makeDeque.prototype.setLength = function () {
return this.length = this.r.length;
};
for (var i=0, methodArray = [
['pushHead', 'unshift'], ['popHead', 'shift'], ['pushTail', 'push'], ['popTail', 'pop']
]; i < methodArray.length; i++) {
makeDeque.prototype[methodArray[i][0]] = (function (i) { // We need to make a function and immediately pass in 'i' here because otherwise, the 'i' inside this function will end up being set to the value of 'i' after it ends this loop as opposed to the 'i' which varies with each loop. This is a common "gotcha" of JavaScript
return function () {
var ret = this.r[methodArray[i][1]].apply(this.r, arguments);
this.setLength();
return ret;
};
}(i));
}
makeDeque.prototype.isEmpty=function() {
return this.r.length === 0;
};
return makeDeque;
}());
If you need to get the length by a length property, as opposed to a method like setLength() which sets it manually after each update, either of the above code samples could be shortened by avoiding the setLength() method, but you'd need to use the Object.defineProperty which does not work (or does not work fully) in older browsers like IE < 9.

Categories

Resources