Beginner and self-taught coder here (always open to learning please correct me) and I'm making a web app through pretty much exclusively HTML CSS and Javascript (I don't really want to use PHP or hosting-side processing because I don't know much about web security and it makes me nervous about uploading data to my hosted site).
Very unsure about the most efficient way to do this so I'm going to try to describe it below and I'd really appreciate your input.
My main question: Is there a more efficient way to do this?
The app eventually will have a javascript canvas, where it will draw an object ('track') at a specific location. This object will then move to another location based off nested data in an array ('step') when the user moves to the next item in an array.
As of now, how I'm going about it is having:
storing the location values in the steps array
have an array of 'tracks' for what shape/color/etc will be drawn on the canvas
linking the two elements by an arbitrary ID that is in both 'steps array' and 'tracks' array
A visual representation of what this might look like
steps[stepNumber].movedTracksInStep[movedTracksInStepNumber] holds object:
{track ID,
X location,
y location}
separate array trackList
trackList[trackNumber] holds object:
{track ID,
shape,
color,
bunchastuff}
I choose to do it like this because I figured it would be better to store the location in the steps array, store the visual data in a separate array, so that way it's not repeating the same data every step.
My question:
Is there a more efficient way to do this, especially in terms of search functions? I'm a newbie so there very well might be something I am missing.
Currently, I just have to search through all of the ID tracks in the step and see if there is a match. I'm wondering if there is a more direct way to link the two together than having to search each time.
I've thought about perhaps having all the data for the visual representation in the first step and then not having to repeat it (though I'm not quite sure how that would work), or having the numbers of arrays match up (but this would change if the user deletes a track or adds a track).
Thank you! Let me know if you need me to explain more.
Objects in JS are stored and copied "by reference", so if you assign value of one object to another, value will not be copied, but reference link will be created. Below is the example close to your code, check inline comments. And you can adopt this behavior to your task:
// Your tracks information
const trackList = {
1: {
shape: "rect",
color: "green",
bunchastuff: "foo"
}
};
// Your steps data
const steps = {
1: {
1: {
// Here we create reference to track 1 in
// trackList object data, without copying it
track: trackList[1],
x: 100,
y: 50
}
}
};
// Print step info
console.log("before track info edit:", steps[1][1].track);
// Update data in track 1
trackList[1].shape = "round";
// Print step info again and we'll
// see, that it also updated
console.log("after track info edit:", steps[1][1].track);
You can read more about object references here: https://javascript.info/object-copy
The structure I have for my firebase database is like this:
fruits:
apple,5
banana,6
I want to put apple and banana in an array so that when i give a command to Google Assistant, it would give me apple, 5 and banana, 6. The code I have is like the one below:
function handleCommand(agent) {
return admin.database().ref('Fruits').child().once("value").then((snapshot) =>{
var i;
var fruitlist=[];
//puts each snapshot child of 'Fruit' in an array
snapshot.forEach(function(item) {
var itemVal = item.val();
fruitlist.push(itemVal);
});
//outputs command in google assistant
for (i=0; i < fruitlist.length; i++) {
agent.add(fruitlist[i]);
}
})
The default response is "not available".
I get the following in the execution logs:
Firebase.child failed. Was called 0 aruguments. expects at least 1.
I do not know which argument to put inside the Firebase.child. if i want all fruits to be "spoken" by Google Assistant. Below is a picture of my firebase structure.
The error looks like the one below:
What I am currently doing now to just output the fruits are manually entering each child in the code like this and removed the ".child" in the return statement:
Which gives me the output below which is also what I want to see but using arrays as the solution I am using now is very much hardcoded:
As the error message suggests, and as you surmise, the child() call expects a parameter - in particular, the name of the child node you want to get information from. However, since you want all the children of the "Fruits" node - you don't need to specify it at all. The child() call just navigates down through the hierarchy, but you don't need to navigate at all if you don't want to.
The snapshot you get back will have a value of the entire object. In some cases, this can be pretty large, so it isn't a good idea to get it all at once. In your case, it is fairly small, so not as big a deal.
On the JavaScript side, you can now handle that value as an object with attributes and values. Your original code didn't quite do what you said you want it to, however - you're getting the value, but ignoring the name (which is the attribute name or key). You can iterate over the attributes of an object in a number of ways, but I like getting the keys of the object, looping over this, getting the value associated with the key, and then "doing something" with it.
While I haven't tested the code, it might look something like this:
function handleCommand(agent) {
return admin.database().ref('Fruits').once("value").then((snapshot) =>{
// Get an object with all the fruits and values
var fruits = snapshot.val();
// Get the keys for the attributes of this object as an array
var keys = Object.keys( fruits );
// Iterate over the keys, get the associated value, and do something with it
for( var i=0; i<keys.length; i++ ){
var key = keys[i];
var val = fruits[key];
agent.add( `The number of ${key} you have are: ${val}` );
}
})
While this is (or should be) working Firebase and JavaScript, there are a couple of problems with this on the Actions on Google side.
First, the message returned might have some grammar problems, so using your example, you may see a message such as "The number of Apple you have are: 1". There are ways to resolve this, but keep in mind my sample code is just a starter sample.
More significantly, however, the call to agent.add() with a string creates a "SimpleResponse". You're only allowed two simple responses per reply in an Action. So while this will work for your example, it will have problems if you have more fruit. You can solve this by concatenating the strings together so you're only calling agent.add() once.
Finally, you may wish to actually look at some of the other response options for different surfaces. So while you might read out this list on a speaker, you may read a shorter list on a device with a screen and show a table with the information. Details about these might be better addressed as a new StackOverflow question, however.
I have the the following data being returned by my api.
[{"category":"Amazon","month":"Feb","total":9.75},
{"category":"Amazon","month":"Mar","total":169.44},
{"category":"Amazon","month":"Apr","total":10.69},
{"category":"Amazon","month":"May","total":867.0600000000001},
{"category":"Amazon","month":"Jun","total":394.43999999999994},
{"category":"Amazon","month":"Jul","total":787.2400000000001},
{"category":"Amazon","month":"Aug","total":1112.4400000000003},
{"category":"Amazon","month":"Sep","total":232.86999999999998},
{"category":"Amazon","month":"Oct","total":222.26999999999998},
{"category":"Amazon","month":"Nov","total":306.09999999999997},
{"category":"Amazon","month":"Dec","total":1096.2599999999998}]
I want to format it so that the months are all grouped under each category like this:
[{"category":"Amazon","month":{"Jan":9.75,"Feb":9.75,"Mar":9.75,"Apr":9.75,etc...}]
How can I do this with javascript?
What I'm ultimately trying to do is to display some pivoted data in a table. I'm not sure what the best design is to accomplish this.
Right now, I'm just setting up a table dynamically and adding in the data corresponding to each row. Are there better design patterns for doing this?
You can reduce the array of objects to an object using the categories as keys, and adding the months, and then map it back to an array again
var arr = [{"category":"Amazon","month":"Feb","total":9.75},
{"category":"Amazon","month":"Mar","total":169.44},
{"category":"Amazon","month":"Apr","total":10.69},
{"category":"Amazon","month":"May","total":867.0600000000001},
{"category":"Amazon","month":"Jun","total":394.43999999999994},
{"category":"Amazon","month":"Jul","total":787.2400000000001},
{"category":"Amazon","month":"Aug","total":1112.4400000000003},
{"category":"Amazon","month":"Sep","total":232.86999999999998},
{"category":"Amazon","month":"Oct","total":222.26999999999998},
{"category":"Amazon","month":"Nov","total":306.09999999999997},
{"category":"Amazon","month":"Dec","total":1096.2599999999998}];
var o = arr.reduce( (a,b) => {
a[b.category] = a[b.category] || [];
a[b.category].push({[b.month]:b.total});
return a;
}, {});
var a = Object.keys(o).map(function(k) {
return {category : k, month : Object.assign.apply({},o[k])};
});
console.log(a);
I would take the following approach:
Write down on a piece of paper how to solve the problem (the "algorithm").
Flesh out this algorithm with more details. Sometimes this is called "pseudo-code".
Convert the pseudo-code into JavaScript.
For instance, your algorithm might look like this:
Create a new thing to hold the results.
Loop through the elements in the input.
For each element in the input, update the results thing.
Return the result.
Sometimes it helps to read out the algorithm aloud to yourself. Or animate the algorithm on a blackboard. Or talk through the algorithm with someone nearby.
The pseudo-code might be:
Create a new array containing a new object to hold the results, with a category property set to "Amazon", and a months property set to an empty object.
Loop through the elements in the input array.
For each element, add a new property to the months property of the results object, whose key is the value of the month property from the element, and whose value is the value of the total property from the element.
Return the result.
If you have specific questions about any of those steps, you can research it further, or ask, such as:
How do I create a new array, with an object inside?
How do I loop through the elements of an array?
How do I retrieve a property from an object?
How do I add a new key/value pair to an object?
How do I return the result?
If you are unfamiliar with any of the terms used above--such as array, object, property, key, value, or element--research them and make sure you know what they mean. Knowing and using correct terminology is the first step to successful programming!
When converting your algorithm into JS, write it step by step, and test it at each phase. For instance, start with a version which doesn't loop over the input at all, and make sure it produces a correct output of [{category: "Amazon", month: {}}]. Walk though your code in the debugger at this and each following step--if you don't know how to use the debugger, learning that should be your first priority! If you want to check a little bit of syntax, to make sure it does what you think, just try it out by typing it into the console. If you don't know what the console is, learning that should be another top priority.
All the above assumes that you've got a single Amazon category. If you are going to have multiple categories, and want multiple objects (one for each) in your output array, then start over from the top and write the algorithm and pseudo-code which can handle that.
I have the following AngularJS code that pulls data from a DB via a backend HTTP request:
// Get the list of functions and associated metrics
$http.get("/BackEnd/Queries/GetFunctionListWithMetrics.php?BuildID=" + $scope.Build.ID + "&ClassID=" + $scope.Class.ID)
.success(function (data, status, headers, config) {
// Save the data
//$scope.functionlist = data;
angular.merge($scope.functionlist, data);
// We've finished loading
$scope.isLoading = false;
})
"data" is an associative array where the key is a number (Typically something like "718214").
Originally my code had the line "$scope.functionlist = data;" (Currently shown commented out) and everything worked fine. I then switched to using the angular.merge line instead (As I was going to fire off several different http requests and I wanted the results all to merge into the same $scope.functionlist object).
From a functionality point of view this worked fine. My page continued to show the data correctly and my multiple HTTP requests could gather the data and merge it together without any timing\ordering concerns.
However for some reason my page started running very slowly. There were no errors shown in the console.
Eventually I worked out what the problem was by adding the following 2 lines and comparing the contentns of functionlist and functionlist2:
$scope.functionlist2 = data;
angular.merge($scope.functionlist, data);
I realised that whilst functionlist2 contained my associative array with about 10 keys, the functionlist version contained an array of 718214 elements!!!
It appears that angular.merge incorrectly interpreted the numeric key of data as an array index.
I've not seen anything about this in the angular documentation or on the web.
Is this a known issue? And is there another way to achieve the correct result (i.e. without 700+k array elements)?
Thanks in advance.
Edit
Ah, Javascript doesn't support associative arrays, only objects. Hmmm, I'm still not sure how to achieve what I need though.
Ah I worked it out in the end and it was actually very simple.
Of course Javascript doesn't actually support associative arrays, just numerically indexed array and objects, so what I actually had was an object.
So in the end all I had to do was initialise $scope.functionlist like this:
$scope.functionlist = {};
instead of how I had been doing it (As an array) like this:
$scope.functionlist = [];
Now it's all working perfectly.
I'm very new to rails, so please bear with me. I haven't quite grasped all the "magic" just yet.
Basically I need to access Location objects in Javascript, which all contain 0-N Coordinates. I'm pretty sure the model is set up right, with each Coordinate having a location_id.
I managed to get a parsed JSON of Location objects using the following method: I put
def index
#locations = Location.all.to_json
end
in the controller, and accessed this in the view with
var theLocations = <%= #locations.html_safe %>;
when I show this variable in the log, however, it doesn't show the Coordinates, which kind of makes sense. Thing is, I was planning to iterate over all the Locations and for each one create a javascript var with the relevant coordinates. These vars would then be passed to Google maps as objects.
Basically I was planning on doing something like (pseudocode):
for(Location l in Locations){
get all Coordinate objects within Location
for(Coordinate c: Coordinates);
var myObject = new google.maps.LatLng(c.X, c.Y),
add myObject to list for this location.
}
}
I'm just not sure how to approach this. How can I get a reference to the Coordinates within the Locations?
The quick and dirty answer is to pass a parameter in your to_json call:
#locations = Location.all.to_json(:include => :coordinate)
You may also want to consider creating a custom JSON structure by overriding as_json and rendering the objects as JSON after that.