This might sound like a newbie question (and probably is) however I am not sure how I should work with this problem:
I create an object like this (js & jquery):
var data = [{ "groupName": "G1", "status": "active"}, { "groupName": "G2", "status": "inactive"}, { "groupName": "G3", "status": "active"}]
function game() {
this.groups = [];
this.makeGroups = function(group) {
this.groups.push(group);
}
this.getGroupsWithStatus = function (status) {
return data.filter( group => group.status == status )
}
}
function group(key) {
this.groupKey = key;
this.showGroupName = function() {
return this.groupKey
}
this.checkPreviousGroup = function () {
var filtered = myGame.getGroupsWithStatus("active"); // 4. calling instance by its name - is it good?
var self = this; // 2. making reference to parent object
var prevGroup;
$.each(filtered, function(group, key) { // 3. filtering with each - is it necessery?
if ( self.groupKey == group.key ) {
prevGroup = key - 1;
return;
}
})
if ( prevGroup.length ) {
return prevGroup
} else {
return false
}
}
}
var myGame = new game();
var groups = myGame.groups // 1. global shortcut to groups
var group1 = new group(0);
var group2 = new group(1);
var group3 = new group(2);
This code should work, however I am almost sure that it is written in a manner it shouldn't be.
Here are my possible problems.
I create shortcut groups to myGame.groups. I've been told that I make a global variable this way (and I suppose I want to) but it is possibly dangerous. Is it?
I use var self = this so I can have a reference to parent object in $.each(). Is there any other way to call parent object in the function referring to another "this"?
I use here $.each to test the expression. Javascript filter function is shorter and better, more readable, but it tests ALL elements and I can break it. In other words if I have 5 elements and I want to filter all elements before 3rd element that has status == "active" is there other way to do it?
I make only one instance of game class called myGame. Should I call it by it's name from other classes or should I do it in other way (i.e. find all instances of game class and assign it to variable?)
Kalreg.
Related
Thanks in advance for any responses:
I don't think this is a duplicate: I reviewed that article in the first comment, that is just a general breakdown of objects and using "this" within javascript.
My other this.function's perform just fine, so I at least have the basics of JS Obj's figured out.
This issue is related to using .map() with a this.function within a constructed object.
The following Google Appscript code uses .map() to update a string in a 2d array. [[string, int],[string, int]]
For some reason, when using .map() it is am unable to access the function "this.removeLeadingZero". If that same function is placed outside of the OBJ it can be called and everything works just fine. For some reason the system claims row[0] is an [object, Object] but when I typeof(row[0]) it returns "string" as it should.
Error: TypeError: Cannot find function removeLeadingZero in object [object Object]. (line 106, file "DEEP UPC MATCH")
Is there any issue using this.function's with .map() inside an object or am I using an incorrect syntax?
function test2DMapping(){
var tool = new WorkingMappingExample()
var boot = tool.arrayBuild();
Logger.log(boot)
}
function WorkingMappingExample(){
this.arr= [["01234", 100],["401234", 101],["012340", 13],["01234", 0422141],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3]];
//mapping appears faster that normal iterations
this.arrayBuild = function(){
var newArray1 =
this.arr.map( function( row ) {
**var mUPC = removeLeadingZero2(row[0])** //working
**var mUPC = this.removeLeadingZero(row[0])** // not working
var index = row[1]
Logger.log(mUPC + " " + index)
row = [mUPC, index]
return row
} )
return newArray1;
};
}; //end of OBJ
//THE NEXT 2 FUNCTIONS ARE WORKING OUTSIDE OF THE OBJECT
function removeLeadingZero2(upc){
try {
if (typeof(upc[0]) == "string"){
return upc.replace(/^0+/, '')
} else {
var stringer = upc.toString();
return stringer.replace(/^0+/, '')
}
} catch (err) {
Logger.log(err);
return upc;
}
}
function trimFirstTwoLastOne (upc) {
try {
return upc.substring(2, upc.length - 1); //takes off the first 2 #'s off and the last 1 #'s
} catch (err) {
Logger.log(err);
return upc;
}
}
Inside the function that you pass to map, this doesn't refer to what you think it does. The mapping function has its own this, which refers to window, normally:
var newArray1 = this.arr.map(function(row) {
// this === window
var mUPC = this.removeLeadingZero(row[0]);
var index = row[1];
Logger.log(mUPC + " " + index);
return [mUPC, index];
});
You have four options:
Array#map takes a thisArg which you can use to tell map what the this object in the function should be:
var newArray1 = this.arr.map(function(row) {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
}, this); // pass a thisArg
Manually bind the function:
var newArray1 = this.arr.map(function(row) {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
}.bind(this)); // bind the function to this
Store a reference to the outer this:
var self = this;
var newArray1 = this.arr.map(function(row) {
// self === (outer this)
var mUPC = self.removeLeadingZero(row[0]);
// ...
});
Use an arrow function:
var newArray1 = this.arr.map(row => {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
});
Additionally, you could stop using this and new.
I have solved this issue and below is the answer in case anyone else runs into this:
this needs to be placed into a variable:
var _this = this;
and then you can call it within the object:
var mUPC = _this.removeLeadingZero(row[0])
Javascript scope strikes again!
I am making a simple hmtl/js game. I'd like to have all the data of the Game in DataofGame. It is like tennis, it is simpler than tennis: there is only set and match. changeinSet is called on click.
But I think i have a problem with private variable so it doesn't work.
Uncaught TypeError: Cannot read property 'WordsoftheGame' of undefined
//Added
document.getElementById('playboutton').addEventListener('click', newGame);
function newGame() {
var DataofGame = new newGameData();
}
// New game
function newGameData() {
this.pointTeam1 = 0;
this.pointTeam2 = 0;
this.WordsoftheGame = ShuffleListe();
this.ASet = new aSet();
}
//How the set is manage ********************
function aSet() {
var oneWord = DataofGame.ListeMot;
// display the word and delete it from the list
document.getElementById('jouer').innerHTML = oneWord[0];
DataofGame.WordsoftheGame.shift();
this.turn = true;
this.score = 0;
}
function changeinSet() {
DataofGame.ASet.score += 1;
//This is the other team's turn:
DataofGame.ASet.turn = !DataofGame.ASet.turn;
};
//shuffle liste
ListOfWords = ['Artiste', 'Appeler', 'Cheval', 'Choisir', 'Ciel', 'Croire', 'Dormir'];
function ShuffleListe() {
data = shuffle(ListOfWords);
return data;
}
function newGameData(){
this.pointTeam1=0;
this.pointTeam2=0;
this.WordsoftheGame= ShuffleListe();
this.ASet=new aSet();
}
//How the set is manage ********************
function aSet(){
var oneWord=DataofGame.ListeMot;
// display the word and delete it from the list
document.getElementById('jouer').innerHTML=oneWord[0];
DataofGame.WordsoftheGame.shift(); // << DataofGame not assigned yet
this.turn=true;
this.score=0;
}
Here when you're accessing DataofGame, it's not yet assigned because you're inside the constructor when calling aSet().
What you want to achieve is not completely clear, but if it's adding an ASet method to your object, you could write something like this:
function newGameData(){
this.pointTeam1=0;
this.pointTeam2=0;
this.WordsoftheGame= ShuffleListe();
this.ASet = function() {
// your code
};
}
NB your coding style for names is a bit messy, you should use uppercases consistently. The usage is to start constructor names with uppercases, the rest in lower cases.
You can let the function return an object with the data or just set the object.
function newGameData(){
return {
pointTeam1 : 0,
pointTeam2 : 0,
WordsoftheGame : ShuffleListe(),
ASet : new aSet()
}
}
But I would recommend to search for how to work with objects in javascript. Maybe this helps:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
So I am pulling in an object that I want to "edit", with a bit of help I have a function that finds the item i'm looking for and replaced the value. What I did no account for when building this was if the items don't exist yet.
So right now the function looks like this :
myFunction = function(moduleName, stateName, startVar){
//currentState brought in from factory controlling it
var currentState = StateFactory.current();
_.each(currentState, function(item) {
if (item.module === moduleName) {
_.each(item.customUrl, function(innerItem) {
if (_.has(innerItem, stateName)) {
innerItem[stateName] = startVar;
}
});
}
});
}
So - this does a great job of replacing the startVar value, assuming it already exists. I need to add some levels of checks to make sure the items exist (and if they don't add them in).
So, for reference, this is what the currentState looks like
[{
"module": "module1",
"customUrl": [
{ "mod1": "2" },
{ "mod2": "1" }
]
}, {
"module": "module2",
"customUrl": [
{ "mod3": "false" },
{ "mod4": "5" }
]
}
];
And so if i passed
myFunction("module1","mod1",3);
This works great, however if I pass
myFunction("module5","mod8","false");
Or maybe something in between like
myFunction("module1","mod30","false");
This function will not handle that scenario. I could use some helpe wrapping my head around how to tackle this problem. Also, am using underscore (if it is required to help). Thank you for taking the time to read!
As mentioned by Phari - something to the effect of this
currentState[moduleName].customUrl[stateName] = startVar;
I was thinking I could just create the object and just _.extend, but because it is an array of objects that wont quite work.
Here's what I mean :
var tempOb = {"module" : moduleName, "customUrl" : [{stateName : startVar}]};
_.extend(currentState, tempOb);
Doesn't quite work right with an array of objects.
It seems that all you need to do is remove the if statement:
if (_.has(innerItem, stateName)) {
innerItem[stateName] = startVar;
}
should become simply:
innerItem[stateName] = startVar;
Then if the property is not present, it will be added. If it is already present, it will be overwritten.
EDIT: to handle absence at top level:
myFunction = function(moduleName, stateName, startVar){
//currentState brought in from factory controlling it
var currentState = StateFactory.current();
var found = false;
_.each(currentState, function(item) {
if (item.module === moduleName) {
found = true;
_.each(item.customUrl, function(innerItem) {
if (_.has(innerItem, stateName)) {
innerItem[stateName] = startVar;
}
});
}
});
if ( ! found ) {
var newItem = {
module: moduleName,
customUrl: []
};
var newInnerItem = {};
newInnerItem[stateName] = startVar;
newItem.customUrl.push(newInnerItem);
currentState.push(newItem);
}
}
I have a program that reads an array of list items. It transforms the array and assigns an explicit listItem Type to each item by using the global variable listItemTypeMap to map the appropriate type. I decided for clarity this should use _.map instead of my own looping logic.
Yet when the code uses map and tries to access listItemTypeMap that variable is now undefined. When I use my own for loop it's perfectly fine. Which leads me to believe there is some kind of scoping issue when using underscore.js. I'm very new to the framework but is there anyway to circumvent or insert the global variable in a _.map object? Or do I have to rewrite how the entire class works?
//One part of the class
var myNewArray= _.map(infoList, this.ReconcileListTypes);
//Another part of the class
MyProject.MyListManager.ReconcileListTypes = function (listItem)
{
var listItemCopy = null;
if (listItem.Value == undefined)
{ //listItemTypeMap is undefined
listItemCopy = _.extend({ "$type": this.listItemTypeMap[listItem.listItemType] }, listItem);
if (listItemCopy.listItemType == listItemType.Exception)
{
if (listItemCopy.Base != null)
{
listItemCopy.Base = _.extend({ "$type": this.listItemTypeMap[listItemCopy.Base.listItemType] }, listItemCopy.Base);
}
}
}
else
{
listItemCopy = _.extend({ "$type": this.listItemTypeMap[listItem.Value.listItemType] }, listItem.Value);
if (listItemCopy.listItemType == listItemType.Exception)
{
if (listItemCopy.Base != null)
{
listItemCopy.Base = _.extend({ "$type": this.listItemTypeMap[listItemCopy.Base.listItemType] }, listItemCopy.Base);
}
}
}
return listItemCopy;
};
Is it possible to create multiple instances of an object in javascript such that they all are ready to be manipulated/edited at the same time? This relates to my earlier question here: Structure of orders in restaurant. What I am trying to do is keep each order object ready for editing until the customer is ready to pay/leave so that new items can be added to or existing items removed from them as desired - and it has to be possible for all order objects at the same time.
If the number of tables isn't very big - say, about 15 - would it be better to create a static array of 15 objects with different table numbers?
Er, yes - trivially (rough code warning):
// your class function
function MyClass(params)
{
this.foo = params.foo;
this.bar = params.bar;
// etc...
}
// object or array to maintain dynamic list o instances
var instances = [];
// create instances in storage object
instances.push(new MyClass({foo:123, bar:456}));
instances.push(new MyClass({foo:'abc', bar:'def'}));
// or alternately by key
instances['mykey'] = new Myclass({foo:'argle',bar'bargle'});
Don't create a static array because there's just no need when a dynamic structure is trivial enough. Perhaps I'm missing something from your question?
Edit: update with more illustrative code based on your earlier question, yet another way to solve the problem.
At this point however this is kind of a teaching thing only. If this was real application I would advise you to model all of this in a server side language - JS is really for controlling UI behaviour not business object modelling.
var Restaurant = {
Order : function (params)
{
this.id = params.id;
this.table = params.table;
this.items = [];
this.number_of_items = 0;
if(!Restaurant.Order.prototype.addItem)
{
Restaurant.Order.prototype.addItem = function (item)
{
// assuming name is unique let's use this for an associative key
this.items[item.name] = item;
this.number_of_items++;
//returning the item let's you chain methods
return item;
}
}
},
Item : function (params)
{
this.name = params.name;
this.quantity = params.quantity;
this.unit_price = params.unit_price;
if(!Restaurant.Item.prototype.price)
{
Restaurant.Item.prototype.price = function ()
{
return this.quantity * this.unit_price;
}
}
},
orders : [],
addOrder : function (order)
{
// assuming id is unique let's use this for an associative key
this.orders[order.id] = order;
//returning the item let's you chain methods
return order;
}
}
with (Restaurant)
{
with (addOrder( new Restaurant.Order({id:123, table:456}) )) // chaining!
{
addItem( new Restaurant.Item({name: 'foo', quantity: 10, unit_price: 10}) );
addItem( new Restaurant.Item({name: 'bar', quantity: 100, unit_price: 1}) );
}
}
var num_items = Restaurant.orders[123].items['foo'].price(); // returns 100
Since you are using object literals on your previous question, I suggest you to give a look to this prototypal inheritance technique, it will allow you to easily create new object instances inheriting from a base instance, for example:
// Helper function
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
// "Base" order object
var order = {
id: 0,
table: 0,
items: []
};
var orders = [], n = 10;
while (n--) { // make 10 objects inheriting from order and add them to an array
orders.push(Object.create(order));
}
Later on, you can access and manipulate your order objects in the orders array:
orders[0].id = 10;
orders[0].table = 5;
orders[0].items.push({
name: "Beer",
quantity: 1,
unit_price: 3
});