I'm trying to learn JavaScript. I'm working on a problem where I'm making a grocery list: an array of sub-arrays that contain item, quantity and price. From there I plan to add items, read back the list, remove items, etc. My current issue is I cannot access sub-methods. I can't seem to find documentation on how to do it - but I keep reading that these methods are private? So maybe I'm way off base. I can't figure out how else I could set this up so multiple methods all talk to each other.
var groceryList = function(food, quantity, price) {
this.item = {};
this.items = {food:food, quantity:quantity, price:price};
return this.items
this.addStuff = function(food, quantity, price) {
this.items.push({food:food, quantity:quantity, price:price});
return this.items
};
this.tallyList = function() {
return items.length;
}
}
var myList = new groceryList("cookie", 2, 1.00);
console.log(myList)
myList.addStuff("brownie", 1, 2.50);
console.log(myList.tallyList);
var groceryList = function(food, quantity, price) {
this.items = [];
this.items.push({food:food, quantity:quantity, price:price});
var that = this;
this.addStuff = function(food, quantity, price) {
that.items.push({food:food, quantity:quantity, price:price});
return that.items;
};
this.tallyList = function() {
return that.items.length;
};
this.getItems = function() {
return that.items;
};
return this;
};
var myList = new groceryList("cookie", 2, 1.00);
console.log(myList);
myList.addStuff("brownie", 1, 2.50);
console.log(myList.tallyList());
This is the correct(-ish) version of what you are trying to accomplish.
Issues with your code:
this.items should be any array, not an object. Notice the square brackets, instead of curly braces.
Your constructor (the groceryList function) is not returning the entire class - it's returning just the list/array. So you cannot call myList.addStuff() or myList.tallyList() - they don't exist!
Please checkout How does the "this" keyword work?. Unlike other OO languages, "this" in JS does NOT refer the current "instance" but the current "scope".
This page describes the usage of "this" better than I could :-)
I have added an extra function (getItems()) - you can now fetch the array of items with
myList.getItems()
That code is kind of weird.
A list is an array in JavaScript and each item in the list you can treat it as an object just as you are doing it.
In your example, groceryList is an object that defines private "methods". Convention in JavaScript and many other programming languages is to name "classes" as UpperCamelCase, so groceryList is GroceryList.
Inside the "function" GroceryList this means the object itself, so you can attach it functions like this.addStuff = function ... or "properties" this.items = []. Arrays variables always call them in plural because they contain a collection of stuff.
this.items is an array (list) of groceries and each grocery has 3 properties: food, quantity and price. And you can add them to they array with the array method this.items.push(grocery) where grocery is a grocery object.
var GroceryList = function() {
this.items = []; // array to store groceries
// add to object method to add groceries to list
this.addStuff = function(food, quantity, price) {
this.items.push({
food: food,
quantity: quantity,
price: price
});
};
};
var groceryList = new GroceryList();
groceryList.addStuff('cookie', 2, 1.00);
groceryList.addStuff('brownie', 1, 2.50);
console.log(groceryList.items);
There is nothing private in this "class". Don't worry about that.
Fiddle to play with http://jsfiddle.net/42cna2d3/.
Related
I am having a little trouble trying to achieve something. So I have some data
let data = [
{
"ID": 123456,
"Date": "2012-01-01",
"Irrelevant_Column_1": 123,
"Irrelevant_Column_2": 234,
"Irrelevant_Column_3": 345,
"Irrelevant_Column_4": 456
},
...
]
And I wanted to remove the irrelevant columns. So someone suggested using map
data = data.map(element => ({ID: element.ID, Date: element.Date}))
The problem is, I dont want to define the columns. I have the user select the columns to keep, and assign them to a variable. I can then do something like
let selectedId = this.selectedIdCol;
The issue is, I am unable to now use this within the map. I am trying
let selectedId = this.selectedIdCol;
this.parsed_csv = data.map(element => (
{ID: element.selectedId, Date: element.Date}
));
But that does not seem to work, just returns the date. Also, my IDE is saying that the variable is unused. So how can I use the selectedId variable as part of the map function?
Thanks
You can do using Bracket notation notation and helper function
Whenever you want to use variable to access property you need to use [] notation.
let data = [{"ID": 123456,"Date": "2012-01-01","column_1": 123,"column_2": 234,"column_3": 345,"column_4": 456},{"ID": 123456,"Date": "2018-10-01", "column_1": 123,"column_2": 234,"column_3": 345,"column_4": 46},]
function selectDesired(data,propName1,propName2){
return data.map(e=> ({[propName1]: e[propName1], [propName2]: e[propName2]}))
}
console.log(selectDesired(data, 'Date', 'column_4'))
The basic technique is illustrated here, assuming that the user's selected column_name is "ID"
let data = [
{
"ID": 123456,
"Date": "2012-01-01",
"Irrelevant_Column_1": 123,
"Irrelevant_Column_2": 234,
"Irrelevant_Column_3": 345,
"Irrelevant_Column_4": 456
}
];
let column_name = "ID";
let curated = data.map(element=>({[column_name]: element[column_name]}));
console.log(curated)
If you are wanting the user to be able to multi-select their columns,(assuming data from above is still in scope)
let user_selection = ["ID","Date"];
let curated = data.map(
(element)=>
{
let item = {};
user_selection.forEach(
(property)=>
{
item[property] = element[property];
}
return item;
}
);
To set up a function that can handle multiple calling situations without having a monstrously hack-and-patched source history, set up the function's signature to receive a spread list of properties.
If you wish to extend the capabilities to accept
a csv property list
an array of property names delivered directly
an array of property names
you can assume the properties argument in the signature to be an iterable of property groupings, having the most basic grouping be a singleton.
Commentary embedded within the sample code to expound in more detail
var getProjection = (data,...properties) =>
{
//+=================================================+
// Initialize the projection which will be returned
//+=================================================+
let projection = {};
//+=================================================+
// Set up the property mapping func
//+=================================================+
let safe_assign = (source, target ,propertyDesignator)=>
{
if(source[propertyDesignator])
{
target[propertyDesignator] = source[propertyDesignator];
}
};
//+=====================================================+
// Iterate the properties list, assuming each element to
// be a property grouping
//+=====================================================+
properties.forEach(
(propertyGroup)=>
{
//+-----------------------------------------------+
// If the propertyGroup is not an array, perform
// direct assignment
//+-----------------------------------------------+
if(!Array.isArray(propertyGroup))
{
//+-------------------------------------------+
//Only map the requested property if it exists
//+-------------------------------------------+
safe_assign(data,projection,propertyGroup);
}
//+-----------------------------------------------+
// If the propertyGroup *is* an array, iterate it
// This technique obviously assumes that your
// property groupings are only allowed to be one
// level deep. This is for accommodating distinct
// calling conventions, not for supporting a deeply
// nested object graph. For a deeper object graph,
// the technique would largely be the same, but
// you would need to recurse.
//+-----------------------------------------------+
if( Array.isArray(propertyGroup))
{
propertyGroup.forEach(
(property)=>
{
safe_assign(data,projection,property);
}
}
}
);
//+===================================+
// Return your projection
//+===================================+
return projection;
};
//+--------------------------------------+
//Now let's test
//+--------------------------------------+
let data = [
{ID:1,Foo:"Foo1",Bar:"Bar1",Baz:"Inga"},
{ID:2,Foo:"Foo2",Bar:"Bar2",Baz:"Ooka"},
{ID:3,Foo:"Foo3",Bar:"Bar3",Baz:"oinga",Floppy:"Floop"},
{ID:4,Foo:"Foo4",Good:"Boi",Bar:"Bar3"Baz:"Baz"}
];
//***************************************
//tests
//***************************************
var projection1 = getProjection(data.find(first=>first),"ID","Baz"));//=>{ID:1,Baz:"Inga"}
var projection2 = getProjection(data[0],["ID","Baz"]);//=>{ID:1,Baz:"Inga"}
var projection3 = getProjection(data[0],...["ID","Baz"]);//=>{ID:1,Baz:"Inga"}
var user_selected_properties = ["ID","Good","Baz"];
var projections = data.map(element=>getProjection(element,user_selected_properties));
//+=====================================+
// projections =
// [
// {ID:1,Baz:"Inga"},
// {ID:2,Baz:"Ooka"},
// {ID:3,Baz:"oinga"},
// {ID:4,Good:"Boi",Baz:"Baz"}
// ];
//+=====================================+
I have a constructor like this
function Employee(name, rank, mf)={
this.name=name;
this.rank=rank;
this.mf=mf;
}
How can I create a new employee and storing the name, rank, and mf in an object with the ability to change it later on? Keep in mind i'm creating the new employee through a function so i can't just create a new var manually. THx.
This is how i create a new employee
function employee(){
var name=prompt("Last, First");
var rank=prompt("Rank");
var mf=prompt("M/F");
var ID=prompt("ID");
var confirming=confirm("ID: "+ID+"Name: "+name+"Rank: "+rank+", "+mf);
if(confirming){
new Employee(name, rank, mf);
}else{
console.log("Employee addition cancled")
}
}
You have a typo in your constructor code which can cause you compilation (syntax) error. Note the equal = sign. Should be
function Employee(name, rank, mf) {
Q: How can I create a new employee and storing the name, rank, and mf in an object with the ability to change it later on?
A: You'll need to maintain a reference to that new object by storing it into a variable. You can achieve it by doing
var myEmployee1 = new Employee(...);
So from there you could access that same object through calling the variable like myEmployee.name
If you are having a function to take on the create employee object role then you can either modify that function to, return the newly created object or populate straight into a global variable. My preference would be the former as it is much cleaner.
Also, tracking the employee objects in an array is 1 strategy you can use but depending on how many objects you are expected. Finding an object in an array may not be as efficient as storing them in a {} dictionary data structure. As you are required to loop through individual objects from an array before finding the right one, whereas dictionary you access objects straight from the key, essentially transversing a tree which is quicker in most scenario.
Obviously storing an employee object through using the name as the key can be dangerous because names are never unique. Instead you should be using the unique identifier Id.
Example:
function employee(){
...
return new Employee(name, rank, mf);
}
var myDictionary = {};
var emp = employee();
myDictionary[emp.id] = emp; // Store the employee object by its key with value = the object itself.
// To access the very same employee object next time. Let say the id is 10 then you would do...
console.log(myDictionary[10].name)
You need to maintain global array for object reference this check my sample code :
var objRef=[];
function employee(){
var name=prompt("Last, First");
var rank=prompt("Rank");
var mf=prompt("M/F");
var ID=prompt("ID");
var confirming=confirm("ID: "+ID+"Name: "+name+"Rank: "+rank+", "+mf);
if(confirming){
objRef[name]=new Employee(name, rank, mf); //access using objRef['alen']
}else{
console.log("Employee addition cancelled.")
}
}
//Your constructor.
function Employee(name, rank, mf)
{
this.name=name;
this.rank=rank;
this.mf=mf;
}
and you can access your object by simply objRef[name]. you can make id as key .
I am looking for the correct way to store references to objects in javascript.
For example I have an object customer:
function Customer(n) {
this.name = n;
}
And an array of all customers, that gets filled:
var customers = new Array()
customers.push(new Customer('Alfred'));
customers.push(new Customer('Bob'));
Now I also have several other objects which reference customers, like purchase, and outstandingOffer, promotion ect. which should all reference to elements of the customers array. For example:
function Purchase(i, c) {
this.customer = c; // ? <- this need to be a reference
this.item = i;
}
This could be done by storing the index in the array, but that seems fragile in case a customer needs to be removed. What is the best way to store a reference to another object in javascript?
looking at below you approach is different
var customers = new Array()
customers.push(new Customer('Alfred'));
customers.push(new Customer('Bob'));
You are pushing new objects in an array without saving a reference to it.So your purchase function will never know what is who or who is what
This is How I would approach it
function Customer(n) {
this.name = n;
this.items=[];
this.addPurchase=function(item){
this.items.push(item);
}
}
The above function will have the follow
The name of the customer
A function that adds an item to the customer item cart
An item cart
var customers = {}; //create a big object that stores all customers
customers.Alfred=new Customer('Alfred'); // create a new object named Alfred
customers.Bob=new Customer('Bob'); // create a new object named Bob
customers.John=new Customer('John'); // create a new object named John
Using console.log, you will get
Alfred: Object, Bob: Object, John: Object
If you want to add items to Alfred you do this
customers.Alfred.addPurchase('pineapple');
If you want to add items to Bob you do this
customers.Bob.addPurchase('mango');
If you want to add items to John you do this
customers.John.addPurchase('coconut');
This is output from console.log(customers.John.items);
Array [ "coconut" ]
So what if we want to delete a customer?
We already have a reference to it!
delete customers.John;
John and this history is gone!...Verify it is deleted
console.log(customers);
output
Object { Alfred: Object, Bob: Object }
use new to create object
var customers = new Array()
customers.push(new Customer('Alfred'));
customers.push(new Customer('Bob'));
function Purchase(i, c) {
this.customer = c; // ? <- this need to be a reference
this.item = i;
}
var Purchase_obj = new Purchase(2,customers[0] );
I'm creating a very simplified version of a drag and drop shopping cart with jqueryui.
My issue is regarding adding data(id, name, price) to an array.
I tried several methodes of adding the data (also an array) to the main container(array). But I keep getting this error: Uncaught TypeError: undefined is not a function
var data = [];
function addproduct(id,name,price){
//var d = [id,name,price];
data[id]["name"] = name;
data[id]["price"] = price;
data[id]["count"] = data[id]["count"]+1;
console.log(data);
}
the addproduct() function can be called by pressing a button
It is not entirely clear to me what type of data structure you want to end up with after you've added a number of items to the cart. So, this answer is a guess based on what it looks like you're trying to do in your question, but if you show a Javascript literal for what you want the actual structure to look like after there are several items in the cart, we can be sure we make the best recommendation.
You have to initialize a javascript object or array before you can use it. The usual way to do that is to check if it exists and if it does not, then initialize it before assigning to it. And, since you're keeping a count, you also will want to initialize the count.
var data = [];
function addproduct(id,name,price){
if (!data[id]) {
// initialize object and count
data[id] = {count: 0};
}
data[id]["name"] = name;
data[id]["price"] = price;
++data[id]["count"];
console.log(data);
}
And FYI, arrays are used for numeric indexes. If you're using property names like "name" and "price" to access properties, you should use an object instead of an array.
And, I would suggest that you use the dot syntax for known property strings:
var data = [];
function addproduct(id,name,price){
if (!data[id]) {
// initialize object and count
data[id] = {count: 0};
}
data[id].name = name;
data[id].price = price;
++data[id].count;
console.log(data);
}
It looks like what you want is an array of objects, although I would need a more detailed description of your problem to be clear.
var data = []
function addproduct(id, name, price)
{
data.push({'id': id, 'name':name, 'price': price, 'count': ++count});
console.log(data);
}
I have an array whose items I want to group, and then display in this grouped fashion. It's all terribly confusing:
App.GroupedThings = Ember.ArrayProxy.extend({
init: function(modelToStartWith) {
this.set('content', Ember.A());
this.itemsByGroup = {};
modelToStartWith.addArrayObserver(this, {
willChange: function(array, offset, removeCount, addCount) {},
didChange: function(array, offset, removeCount, addCount) {
if (addCount > 0)
// Sort the newly added items into groups
this.add(array.slice(offset, offset + addCount))
}
});
},
add : function(things) {
var this$ = this;
// Group all passed things by day
things.forEach(function(thing) {
var groupKey = thing.get('date').clone().hours(0).minutes(0).seconds(0);
// Create data structure for new groups
if (!this$.itemsByGroup[groupKey]) {
var newArray = Ember.A();
this$.itemsByGroup[groupKey] = newArray;
this$.get('content').pushObject({'date': groupKey, 'items': newArray});
}
// Add to correct group
this$.itemsByGroup[groupKey].pushObject(thing);
});
}
});
App.ThingsRoute = Ember.Route.extend({
model: function() {
return new App.GroupedThings(this.store.find('thing'));
},
});
This only works if I use the following template:
{{#each model.content }}
These don't render anything (an ArrayController is used):
{{#each model }}
{{#each content }}
{{#each}}
Why? Shouldn't the ArrayController proxy to "model" (which is GroupedThings), which should proxy to "content"?
The reason this becomes a problem is that I then want to sort these groups, and as soon as I change the entire contents array (even using ArrayProxy.replaceContent()), the whole views rebuilt, even if only a single item is added to a single group.
Is there a different approach to this entirely?
I've tended to use ArrayProxies slightly differently when doing such things.
I'd probably get Ember to do all the heavy lifting, and for sorting get it to create ArrayProxies based around a content collection, that way you can sort them automatically:
(note I haven't run this code, but it should push you off in the right direction)
App.GroupedThings = Em.ArrayProxy.extend({
groupKey: null,
sortKey: null,
groupedContent: function() {
var content = this.get('content');
var groupKey = this.get('groupKey');
var sortKey = this.get('sortKey');
var groupedArrayProxies = content.reduce(function(previousValue, item) {
// previousValue is the reduced value - ie the 'memo' or 'sum' if you will
var itemGroupKeyValue = item.get('groupKey');
currentArrayProxyForGroupKeyValue = previousValue.get(itemGroupKeyValue);
// if there is no Array Proxy set up for this item's groupKey value, create one
if(Em.isEmpty(currentArrayProxyForGroupKeyValue)) {
var newArrayProxy = Em.ArrayProxy.createWithMixins(Em.SortableMixin, {sortProperties: [sortKey], content: Em.A()});
previousValue.set(itemGroupKeyValue, newArrayProxy);
currentArrayProxyForGroupKeyValue = newArrayProxy;
}
currentArrayProxyForGroupKeyValue.get('content').addObject(item);
return previousValue;
}, Em.Object.create());
return groupedArrayProxies;
}.property('content', 'groupKey', 'sortKey')
);
You'd then Create a GroupedThings instance like this:
var aGroupedThings = App.GroupedThings.create({content: someArrayOfItemsThatYouWantGroupedThatHaveBothSortKeyAndGroupKeyAttributes, sortKey: 'someSortKey', groupKey: 'someGroupKey'});
If you wanted to get the groupedContent, you'd just get your instance and get.('groupedContent'). Easy! :)
...and it'll just stay grouped and sorted (the power of computed properties)... if you want an 'add' convenience method you could add one to the Em.Object.Extend def above, but you can just as easily use the native ones in Em.ArrayProxy, which are better IMHO:
aGroupedThings.addObjects([some, set, of, objects]);
or
aGroupedThings.addObject(aSingleObject);
H2H