Passing multiple objects to function parameter at once - javascript

I'm writing a program (in JavaScript) that will determine the most suitable car based on a user's needs. I have 4 objects, each one a different car. For simplification purposes I will provide one object as an example. Assume there are 3 other objects with the same properties in my code (Fiat, Audi, BMW, etc.).
var chevy = {
make: "Chevy",
model: "Bel Air",
year: 1957,
color: "red",
passengers: 2,
convertible: false,
mileage: 1021
};
The goal is to pass each object as an argument to a function, and return a boolean value based on conditionals. Here is the function:
function prequal(car) {
if (car.mileage > 10000) {
return false;
}
else if (car.year > 1960) {
return false;
}
else {
return true;
}
}
And calling the function:
var worthALook = prequal(taxi);
if (worthALook) {
console.log("You gotta check out this " + taxi.make + " " + taxi.model);
}
else {
console.log("You should really pass on the " + taxi.make + " " + taxi.model);
}
Do I have to call each object seperately? Or is there a simplified way of calling the function for all 4 objects at once? I'm new to this and working through this problem spiked my curiosity.
Thanks!!
EDIT: Sorry for the rambling but I seem to have figured out a solution. I am getting the desired output by using a nested function:
function worthALook(car) {
var shouldYouBuy = prequal(car);
if (shouldYouBuy) {
console.log("You gotta check out this " + car.model);
}
else {
console.log("You should really pass on this " + car.model);
}
}
calling the original 'prequal' function (see above) inside the 'worthALook' function outputs:
You should really pass on this Taxi
You should really pass on this Cadillac
You gotta check out this Bel Air
You should really pass on this 500
After each object I called the worthALook function like so:
worthALook(chevy);
worthALook(fiat);
etc.
I received my desired output but does my code seem like overkill?
Thanks!

You could put the objects in an Array and filter what's worth a look. Return that object instead of a boolean.
function worthALook(){
return ([].filter.call(arguments, prequal) || [false])[0];
/* remove [0] from the above line and change [false] to false if you
want to return multiple cars that satisfy a condition. Use indexes if you do that */
}
var car = worthALook(chevy, fiat, audi, bmw); // pass inas many as you want
if(car) // check if it's a car
console.log("You gotta check out this " + taxi.make + " " + taxi.model);
The return statement is used with || so even if none of the cars satisfy the condition, then you could return false. Also, we are using arguments object, so you can pass in as many car objects to the worthALook function, which will make use of prequal function to filter 'em.

Related

How to pass multiple variables from one function to another function by using an object

Problem
I have the issue where I need to pass multiple variables from one function to another. And both variables that need to be passed come from the same initial function.
I can pass one variable between objects ok (not in an ojbect), but passing multiple variables doesn't seem to work when I try and pass them as an object, which seemed to be the closest source for what I'm after here...
What works
Here's the snippet of the code that is working for passing a single variable from one function to another...
function processFruits() {
//runs the getFruits function first to obtain the variable for processing in the next function
getFruits();
//runs the retrieveInfo function using the fruitType variable from the initial function
retrieveInfo(getFruits(fruitType));
}
function getFruits() {
//does something which get the fruit type and number of fruits
fruitType = 'bananas';
fruitNumber = matches[0];
return fruitType;
}
function retrieveInfo(b) {
console.log("The fruit is: " + b);
}
//Returns 'The fruit is: bananas'
What doesn't work
Here's the snippet of code that isn't working:
function processFruits() {
//runs the getFruits function first to obtain the variables for processing in the next function
getFruits();
//runs the retrieveInfo function and tries to pass the variables from getFruits as variables within an object
retrieveInfo(getFruits.fruitType, getFruits.fruitNumber);
}
function getFruits() {
//does something which get the fruit type and number of fruits
let fruitType = 'bananas',
fruitNumber = matches[0]; //let's say this is returning the number '5'
return { fruitType, fruitNumber };
}
function retrieveInfo(b, c) {
console.log("The fruit is: " + b);
console.log("The number of desired fruit is: " + c);
}
This returns 'The fruit is: undefined'
AND
'The number of desired fruit is: undefined'...
Whereas, it needs to return 'The fruit is: bananas'
AND
'The number of desired fruit is: 5'...
To reiterate, the script just needs to be able to pass multiple variables from one function to another for the snippet above, so it ends up giving the desired outcome...
Thanks in advance
You need to access the properties of the return value of the function. Destructuring assignment is convenient for this.
function processFruits() {
let {fruitType, fruitNumber} = getFruits();
retrieveInfo(fruitType, fruitNumber);
}
function getFruits() {
let fruitType = 'bananas',
fruitNumber = 1;
return { fruitType, fruitNumber };
}
function retrieveInfo(b, c) {
console.log("The fruit is: " + b);
console.log("The number of desired fruit is: " + c);
}
processFruits();

Javascript- Passing variable as method

My issue is trying to pass a variable defined via prompt into an object method. At the end of my code I try to pass variable "house" into the ".teamPick" method in my class constructor but no-go. If I console.log(house) it prints 'gryffindor' but when I try to pass house.teamPick() I get an error saying "house.teamPick() is not a function." I am at my wit's end.
function House(color,broom) {
this.color= color;
this.broom= broom;
this.teamPick= function() {
alert("Throw on your"+" "+ this.color + " " + "robes, jump on your \n" + this.broom + ", " + "and let's play some Quidditch!")
};
};
var gryffindor= new House('red', 'Firebolt');
var house= prompt('What team do you choose?').toLowerCase();
if(house=== "gryffindor") {
house.teamPick();
};
You are using the wrong value (house) at the end of your script. house is a string value and not an instance of House.
gryffindor is an instance of House (created with new House()), so that's what you want to use instead.
You want the last three lines to be something like this:
if(house === "gryffindor") {
gryffindor.teamPick();
};

Get object in array with underscore.js

I have a function with a rather convoluted object in this format:
function getNBATeamsESPNByAbbrev(abbrev)
{
var json = {
"sports":[
{
"name":"basketball",
"id":40,
"uid":"s:40",
"leagues":[
{
"name":"National Basketball Assoc.",
"abbreviation":"nba",
"id":46,
"uid":"s:40~l:46",
"groupId":7,
"shortName":"NBA",
"teams":[
{
"id":1,
"uid":"s:40~l:46~t:1",
"location":"Atlanta",
"name":"Hawks",
"abbreviation":"ATL",
},
{
"id":2,
"uid":"s:40~l:46~t:2",
"location":"Boston",
"name":"Celtics",
"abbreviation":"BOS",
"color":"006532",
},
]
}
]
}
],
"resultsOffset":0,
"resultsLimit":50,
"resultsCount":30,
"timestamp":"2014-03-22T23:42:43Z",
"status":"success"
}
obj = _.find(json.sports[0].leagues[0].teams, function(obj) { return obj.abbreviation == abbrev })
if (obj !== undefined)
{
var team = new Object();
team.abbrev = abbrev;
team.location = obj.location;
team.nickname = obj.name;
return team;
}
}
It can be easier seen at this example JSFiddle. So I have the team abbreviation, and I want to pull back the team object as a whole (this is a stripped down version, leaving only the necessary details). This seems to work fine. However, another case has arisen, one in which I need to pull back the team object based on its location + " " + name. So I tried to do the same thing using underscore.js, passing in the parameter name, and changing the predicate in ._find to return obj.location + " " + obj.name == name. For example, I'd pass in Atalnta Hawks as name and expect to return the relevant team object. Here's a very similar JSFiddle demonstrating the change. But, now it can't seem to find the team object I want to pull. Is it because such a string concatenation isn't allowed in underscore.js, or is there something stupid I'm missing?
Line 50, you have:
team.abbrev = obj.abbrev;
and it should be
team.abbrev = obj.abbreviation;

jquery JSON array as localStorage item

Little change of my code and it's partially works :)
var db = {
hotels: JSON.parse(localStorage.getItem('table') || "[]"),
get objects() {return this.hotels},
set objects(obj) {
obj = this.hotels;
localStorage.setItem('table', JSON.stringify(obj))
}
}
jQuery(function(){
var count = localStorage.getItem('count');
if(!count) {
count = 0;
}
function Add(item){
var client = {
ID : jQuery(item).find("#txtID").val(),
Name : jQuery(item).find("#txtName").val(),
Photo : jQuery(item).find("#txtPhone").val(),
Link : jQuery(item).find("#txtEmail").val()
};
db.objects = db.objects.push(client);
count = count+1;
localStorage.setItem('count',count);
jQuery('.panel2 a span').text('('+ localStorage.getItem('count') +')');
jQuery(item).find('.add_rem').hide();
jQuery(item).find('.remove').show();
jQuery("#tblList .empty").hide();
jQuery("#tblList").find('li:gt(0)').remove();
jQuery.each(db.objects,function(i,element) {
jQuery("#tblList").append("<li class='added"+db.objects[i].ID+"'>"+
"<img src='../../images/general/delete.gif' alt='Delete"+i+"' class='delete'/>" +
"<a href='"+db.objects[i].Link+"' title='"+db.objects[i].Name+"'>"+
" <img src='"+db.objects[i].Photo+"' alt='"+db.objects[i].Name+"'>" +
" <span>"+db.objects[i].Name+"</span>" +
" </a>" +
"</li>");
})
return true;
}
function Delete(item){
jQuery(item).prev('.add_rem').show();
jQuery(item).find('.remove').hide();
jQuery(item).find('.remove').removeAttr('alt');
}
function List(){
if(count > 0) {
jQuery("#tblList .empty").hide();
jQuery('.panel2 a span').text('('+ localStorage.getItem('count') +')');
}
for(var i= 0; i<= count; i++) {
var cli = JSON.parse(db.hotels);
if(cli[i] != null){
jQuery("#"+cli[i].ID).find('.add_rem').hide();
jQuery("#"+cli[i].ID).find('.remove').show();
jQuery("#"+cli[i].ID).find('.remove').attr('alt','Delete'+i);
jQuery("#tblList").append("<li class='added"+cli[i].ID+"'>"+
"<img src='../../images/general/delete.gif' alt='Delete"+i+"' class='delete'/>" +
"<a href='"+cli[i].Link+"' title='"+cli[i].Name+"'>"+
" <img src='"+cli[i].Photo+"' alt='"+cli[i].Name+"'>" +
" <span>"+cli[i].Name+"</span>" +
" </a>" +
"</li>");
}
}
}
jQuery("#frmCadastre").bind('submit',function(e){
e.preventDefault()
return Add(this);
});
List();
jQuery(".remove, .delete").bind("click", function(e){
e.preventDefault();
Delete(this);
List();
});
})
now my question is how to push element to array after page refresh this is located in function Add()
array looks like this
"["{"ID":"1","Name":"test","photo":"/link/to/photo.jpg"}"]"
and if i add another element before page refresh it works great
"["{"ID":"0","Name":"test0","photo":"/link/to/photo0.jpg"}","{"ID":"1","Name":"test1","photo":"/link/to/photo1.jpg"}"]"
but if i Reload page and try to add an element Firebug is throwing:
`TypeError: db.objects.push is not a function
db.objects = db.objects.push(client);`
We will start to clear out what happens with HTML5 Local Storage. Local storage is a place in your disk defined by every browser that supports it. These places may be different for every browser. They hold tuples with keys and values, both strings. If you want to save a whole object to the disk, you must serialize it. That means you have to transform it into an array of data, in our case chars -> string. In javascript the most common object serialize function is JSON.stringify. Its input is a valid JSON object, which in our case is an array and it will make it into a string using the literals you use to initialize an object like {x:5}. JSON.stringify([{x:5},{x:6},{x:7}]) will have the following output: "[{x:5},{x:6},{x:7}]". And to reconstruct your object from a string you use JSON.parse(x) where x is a valid json string. You want now to have an array of objects, the first thing you'll think of is to serialize your array you have as a var in your program and add a special key you remember to store it into your disk. Each browser has seperate localStorage for every site that is hosted by a server.
An example that stores a value bound to a key in localstorage is this:
localStorage.setItem('x','5');
localStorage['x'] = 5;
localStorage.x = 5;
all of them do the same thing, and their speed is in descending order. Now you have at Chrome->resources->localstorage:
+-------------------+
| Key | Value |
+---------+---------+
| x | "5" |
+---------+---------+
When you make your first visit to the page, you have nothing in localStorage, so you must have some initial values. Trying to get a value by:
return localStorage.getItem('x');
return localStorage['x'];
return localStorage.x;
will give you undefined. There is a nice operator made in javascript and is the ||.
null || 5 //returns 5
undefined || 3.14 //returns 3.14
'' || 6 //returns 6
[] || {} //returns []
If the left operand "exists", return it, else return the right one. This makes things faster with localStorage.getItem('x') || 5 so if a tuple with key x exists it will return the value of the item with the specified key, else it will return the 5, which is our initial value.
Let's get back to the localStorage again. Remember the tuples are saved into the disk, which is vastly slower to access than things in ram. If I want to read the value of an item in the localStorage let say in a loop, several times, should I read it directly from the disk, or should I read it once from the disk and save it into ram to access it faster? You surely know what makes more sense...So I must have a variable that is the clone of the one in the localStorage. Let's say I name it private_var. It must have an initial value which will be:
var private_array = JSON.parse(localStorage.getItem('array')) || [];
When you want to change your array in localstorage (e.g pushed an item) you use:
private_array.push(item)
localStorage.setItem('array', JSON.stringify(private_array))
Your localstorage will be like:
+---------+-----------------------------------------+
| Key | Value |
+---------+-----------------------------------------+
| array | [{"name":"george", "surname":"bush"}] |
+---------+-----------------------------------------+
To make things faster in terms of code production, not program speed you can define setters and getters.
var obj = {
temp: 5,
get x( ) { return this.temp },
set x(value) { this.temp = value }
}
we have an object named obj, a member temp and a setter and a getter function, just like in some java code. You can check obj.temp === 5. These special operators allow us to write
obj.x = obj.x + 6;
and it will be executed as something like this:
obj.set_x(obj.get_x() + 6);
So let's say you have an interface named db (database, similar to what a locastorage is) and a "private member" which is clearly not. You can see an implementation at http://ejohn.org/blog/javascript-getters-and-setters/ with __define(G/S)etter__ which has real private member, but this one is surely faster to write and more readable.
var db = {
cl_arr: JSON.parse(localStorage.getItem('array')) || [],
get clients( ) { return this.cl_arr },
set clients(v) {
localStorage.setItem('array', JSON.stringify(this.cl_arr));
if(v.constructor === Array) { this.cl_arr = v }
}
}
so when I execute:
db.clients.filter(function(client) { return client.money > 1000 });
this will be executed
db.get_clients().filter...;
And when I try to change the array, I will write this
db.clients = db.clients.push(client);
Even if the push method can change the array, only the get function will be triggered so only the "private" cl_arr variable will change, to trigger the setter which updates our localStorage too I have to write the db.clients = ...

Check if a object is defined, best practice.

I have the following JSON response from a ajax-request.
var json = {
"response": {
"freeOfChargeProduct": {
"description": "Product",
"orderQty": 5,
"productName": "XYZ",
"qty": 6,
"details": {
"price": 55.5,
"instock": "true",
"focQuantity": 1
}
},
"orderLineId": 4788,
"totalOrderLinePrice": "741.36",
"totalOrderPrice": "1,314.92",
"totalQty": 17
};
The JSON dosen't always return a "freeOfChargeProduct" property. So if I want to get the "freeOfChargeProduct" price, then I have to do the following:
var getFreeOfChargeProductPrice = function() {
var r = json.response;
if (r && r.freeOfChargeProduct && r.freeOfChargeProduct.details) {
return r.freeOfChargeProduct.details.price;
}
return null;
};
No problems. But it's very annoying to check every property in the object, so I created a function that check if a property in a object is defined.
var getValue = function (str, context) {
var scope = context || window,
properties = str.split('.'), i;
for(i = 0; i < properties.length; i++) {
if (!scope[properties[i]]) {
return null;
}
scope = scope[properties[i]];
}
return scope;
};
var price = getValue('json.response.freeOfChargeProduct.details.price');
// Price is null if no such object exists.
Now to my question: Is this a good or bad way to check if a property exists in an object? Any better suggestions/methods?
EDIT:
I don't wan't to use the &&-operator. I am lazy and I'm looking for a reusable method to check if a object (or property of a object) is defined.
:) Thanks!
Use the guard pattern:
if (json.response && json.response.freeOfChargeProduct && json.response.freeOfChargeProduct.details) {
// you can safely access the price
}
This is how the guard pattern works.
if (a && a.b && a.b.c) { ... } else { ... }
The first check is "Does the property a exist?". If not, the else-branch gets executed. If yes, then the next check occurs, which is "Does object a contain the property b?". If no, the else-branch executes. If yes, the final check occurs: "Does the object a.b contain the property c?". If no, the else-branch executes. If yes (and only then), the if-branch executes.
Update: Why is it called "guard pattern"?
var value = a && b;
In this example, the member b (the right operand) is guarded by the && operator. Only if the member a (the left operand) is truthy ("worthy"), only then the member b is returned. If, however, the member a is falsy ("not worthy"), then it itself is returned.
BTW, members are falsy if they return these values: null, undefined, 0, "", false, NaN. Members are truthy in all other cases.
if(x && typeof x.y != 'undefined') {
...
}
// or better
function isDefined(x) {
var undefined;
return x !== undefined;
}
if(x && isDefined(x.y)) {
...
}
This will work for any data type in JavaScript, even a number that is zero. If you are checking for an object or string, just use x && x.y within the if statement, or if you already know that x is an object, if(x.y) ...
You could do something like this:
try{
var focp = json.response.freeOfChargeProduct
var text = "You get " + focp.qty + " of " +
focp.productName +
" for only $" + (focp.qty-focp.details.focQuantity)*focp.details.price +
", You save $" + focp.details.focQuantity*focp.details.price;
$("order_info").innerText = text;
} catch(e) {
// woops, handle error...
}
It would generate a message like this from the provided data in your question if the fields exists:
You get 6 of XYZ for only $277,5, You save $55.5
If the data is non-existing, you'll end up in the catch block. You could always just to a Try, Catch, Forget here if you can't come up with a way to handle the error (Maybe do a new AJAX request for the data?).
This is not a syntax issue as it is a design pattern issue.
Question A.
* Do you have control of the json server?
If the answer to this is no, which I assume, the situation will be all on the client.
Please read this:
http://martinfowler.com/eaaDev/PresentationModel.html
As the server is the source, in this case it will provide the model.
This pattern specifies an additional artifact: The presentation model (PM). In javascript i would suggest two artifacts, a additional for the convertor code.
According to this design pattern the PM is responsible for converting the model to the PM, and back again if necessary. In your case no conversion from PM to M will ever occur.
This means that a js object has a method or constructor that digest the model and translate itself, with the help of the convertor (below).
Doing this you will end up with a PM looking like this:
var OrderlinePM = {
"hasFreeOfCharge": false | true,
"freeOfCharge" : {...}
`enter code here`
this.getFreeOfCharge = function() {
...
}
this.fromModel = function(jsonEntry, convertor) {
//convert this with the convertor ;) to a for this specific view usable OrderlinePM
// also inwith
...
}
enter code here
"orderLineId":0,
"totalOrderLinePrice":"741.36",
"totalOrderPrice":"1,314.92",
"totalQty":17
};
function mySpecialFunctionPMConvertor {
this.fromModel = function() {
... //do strange stuff with the model and poulate a PM with it.
}
}
Ok, I give up trying to format code in this rich text editor :(
You can have several PM:s for diffrent tasks all depending on the same model object.
In addition this will make the converter object testable in something that could be automatically executed.... err ok maby manually, but anyway.
So the problem of the cumbersome reflection code is really not a problem. But cohesion is a issue, expessially in JavaScript.

Categories