Pass a Map as argument to another function in JavaScript - javascript

I just had an interview test in which I had to build a Route suggestion feature using ReactJS. Long story short, I tried to use the Dijkstra's algorithm to find the shortest path between 2 train stations in a network.
In my implementation, I maintained a tracingMap which is a Map object that stores a station X as key. The value of this Map is another station Y which is a neighbor of X that is on the shortest path from the origin station to X. My goal is to recursive call tracingMap.get(destStation) while destStation != origStation in the end in order to rebuild the shortest path from the origin station to destination station.
The skeleton is as following:
// Store all the StationNode using stationId as key
let stationGraph = new Map();
function findShortestPath(origStationId, destStationId) {
let tracingMap = new Map();
... perform the Dijkstra's algorithm ...
return buildRoute(tracingMap, origStationId, destStationId);
}
function buildRoute(tracingMap, origStationId, destStationId) {
...
}
Here is my problem. I have a StationNode class which is my own custom object to implement the graph of stations. Initially, I tried to use this class as both key and value of my tracingMap. I tried to use console.log(tracingMap) at the end of the findShortestPath() function to make sure the map was updated properly and I could see all the entries I added just fine. I even tried to call console.log(tracingMap.get(destStationNode)) to be 10000% sure that I can get the value I need.
However, when the tracingMap arrives at the buildRoute() function, tracingMap.get(stationGraph.get(destStationId)) returns undefined. I couldn't get any values out of it even though console.log(tracingMap) is still showing that ALL entries are still there.
In the end, I had to change my implementation to stores stationId as key and value in the tracingMap. Then it would work just fine when I pass this Map to the other function.
As I'm relatively inexperienced in JavaScript, I'd be very grateful if you could tell me what I've done wrong. I believe I have a wrong understanding somewhere :).

Related

Most efficient way to link an object in one array with an object in another array

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

javascript - Set vs Map - which is faster?

Set and Map both are newer data types in es6 and for certain situations both can be used.
e.g - if i want to store all unique elements, i can use Set as well as Map with true as value.
const data: string[] ;
// console.log('data', data[0])
const set = new Set();
const map = new Map<string, boolean>();
data.forEach((item) => {
map.set(item, true);
});
data.forEach((item) => {
set.add(item);
});
Both works, but i was wondering which one is faster ?
Update 1
I am looking for which of the data structure is faster in case of storing data.
checking if value exist using -
map.has(<value>)
set.has(<value>)
deleting values
Also i can understand true is redundant and not used anywhere, but i am just trying to show how map and set can be used alternatively.
What matters is speed.
In the most basic sense:
Maps are for holding key-value pairs
Sets are for holding values
The true in your map is completely redundant ... if a key exists, that automatically implies, that it is true/exists - so you will never ever need to use the value of the key-value pair in the map (so why use the map at all, if you're never gonna make use of what it is actually for? - to me that sounds like a set/array with extra steps)
If you just want to store values use an array or set. - Which of the two depends on what you are trying to do.
The question of "which is faster" can't really be answered properly, because it largely depends on what you are trying to do with the stored values. (What you are trying to do also determines what data structure to use)
So choose whatever data structure you think fits your needs best, and when you run into a problem that another would fix, you can always change it later/convert from one into another.
And the more you use them and the more you see what they can and can not do, the better you'll get at determining which to use from the start (for a given problem)

Undefined index... but it says it's defined in the debugger

(EDIT: I solved my issue! Though I still don't understand the situation I see in the debugger. See my answer for more details)
(TL;DR: index is always undefined when used with a certain array. Doubt that would be enough info, but maybe for someone who's experienced this before.)
So basically, I'm using an array in javascript, and I started noticing some odd behaviour, so I went to the debugger, and I found that a defined variable representing the index was being treated as undefined. It's ONLY the case with this specific array, and it's index. I don't get errors saying that it's undefined, but when I look in the debugger, it says it's undefined when I hover over the variable in the array call (but it's defined if I hover over it anywhere before the array call), and I'm getting bugs that make it clear that the array is not being used properly. It makes absolutely no sense to me, but maybe someone's encountered a similar issue.
Take this example of code, It's drawing a tilemap layer for my MapRenderer class. The culprit here is "this.Map.layers". When I go into this function in the debugger, layerIndex is defined if I hover over the function parameter, but if I hover over it on the array call, it says it's undefined, and the whole logic breaks.
DrawLayer(ctx, camPos, layerIndex)
{
// Get the map/tile position based on the camera position, to decide which tile to start drawing.
var mapCamPos = new Point(Math.floor(camPos.x/TILESIZE),
Math.floor(camPos.y/TILESIZE));
// Get the max tile position based on camera position, to decide where to stop drawing.
var camPosLimit = new Point(Math.ceil(this.DrawSize.x/TILESIZE)+mapCamPos.x,
Math.ceil(this.DrawSize.y/TILESIZE)+mapCamPos.y);
// loop through all tiles we need to draw using rows and columns.
for(var row=mapCamPos.y;row<this.Map.layers[layerIndex].height&&row<=camPosLimit.y;row++)
{
for(var col=mapCamPos.x;col<this.Map.layers[layerIndex].width&&col<=camPosLimit.x;col++)
{
var currentTileID = this.GetTileID(layerIndex, row, col);
if (currentTileID >= 0 && !isNaN(currentTileID))
{
var drawPos = new Point(((col*TILESIZE)-camPos.x), ((row*TILESIZE)-camPos.y));
this.Spritesheet.PlayFrame(currentTileID);
this.Spritesheet.Draw(ctx, drawPos);
}
}
}
}
This is happening in many instances of my code wherever I'm using that array. I want to add how this started, because all of this logic was working previously. I had my tilemap working with multiple csv files, which I loaded as 2d arrays into an array. Today, I decided to switch it all to use one json file, as it is simply cleaner (one file rather than one csv per map layer), and I can add extra properties and stuff in the future rather than just having the tileIDs. So, in the above example, this.Map gets initialized through an ajax call(using jquery) to read the json file, before DrawLayer ever gets called. Still, I don't see why this would cause this. Doing "mapRenderer.Map.layers" in the console tells me that it's a normal array, and when I try calling it normally from the console, it works fine. I'm so confused at this issue. I had literally the same function before and it worked, just that my array has changed a bit(it used to be just "this.Layers", instead of "this.Map.layers"), but it's still a normal array... I don't see why it would behave so differently just because it was generated via json...
Any help or explanations would be greatly appreciated, thanks.
I still don't understand the situation I see in the debugger, maybe it's a firefox bug, or feature I don't understand. But I managed to fix my issue. It was a basic logic bug: I'm using the "Tiled" map editor, and when you export those maps to CSVs, the tile IDs are zero-based, meaning empty tiles are -1. When you export to json, they aren't zero-based, meaning empty tiles are 0, which I failed to notice, and this was the root of all my current issues. If anyone can explain why the firefox debugger might say defined variables are "undefined" when you hover over them, that would still be good to know.

How to access object in parsed nested JSON in Google Apps Script

Broad spectrum apology, to start off with, for any mistakes relating to being a n00b when it comes to code, js, Google Apps Script and indeed stackoverflow.com.
I am trying to extract the polyline from an API call to Google Maps' Directions API. Parsed from the API results, I get:
{routes=[{summary=A215, copyrights=Map data ©2017 Google, legs=[{duration={text=18 mins, value=1108}, start_location={lng=-0.1295712, lat=51.4227696}, distance={text=7.2 km, value=7187}, start_address=London SW16 6ET, UK, end_location={lng=-0.0706306, lat=51.3869032}, duration_in_traffic={text=15 mins, value=882}, end_address=Woodside Green, London SE25 5EU, UK, via_waypoint=[], steps=[{duration={text=1 min, value=6}, start_location={lng=-0.1295712, lat=51.4227696}, distance={text=29 m, value=29}, travel_mode=DRIVING, html_instructions=Head <b>north</b> on <b>Streatham High Rd</b>/<b>A23</b> toward <b>Streatham Common N</b>/<b>A214</b>, end_location={lng=-0.1297011, lat=51.42301399999999}, polyline={points=iozxHxhXo#X}},....
My script is as follows:
function listJourneyPoints(value) {
//Temporary url for var value
var value = "https://maps.googleapis.com/maps/api/directions/json?origin=SW16%206ET&destination=SE25%205EU&mode=driving&region=UK&departure_time=1507676400&key=AIzaSyDbswrt7RFy3s13ulO9BUY9jQUNchUfi4o";
//Call the google route planner api
var routeResponse = UrlFetchApp.fetch(value);
//Parse JSON from routeResponse
var json = routeResponse.getContentText();
var data = JSON.parse(json);
//Get the polyline
var polyl = data["routes"]["legs"]["steps"]["polyline"];
Logger.log (polyl);
}
I can't work out how to navigate through the arrays and objects to the polyline. I know that dot notation doesn't work with arrays so have used square brackets, but whatever I try I cannot progress past data["routes"]["legs"] without getting 'TypeError: cannot read property "steps" from undefined'. I'm aware the problem probably lies with my not understanding the data structure, but after hours of searching, including using an online Json Parser to elucidate the structure, I'm still stuck, so would be grateful for any help. Thanks!
When working with complicated nested JSON, I like to Logger.log() multiple times as I navigate through it to get a better look at what's going on. Even without that though, adding some formatting to the JSON to see the structure of it and removing unneeded entries can both be helpful in seeing how to access specific elements:
{
routes: [{
legs: [{
steps: [{
polyline: {
points: iozxHxhXo#X
}
}]
}]
}]
}
So the first thing to notice is that routes holds an array, which means we need to access its indices to access the nested objects. Luckily, all of the nested elements are wrapped in an object so we can just access the first element of the array: data["routes"][0]. If I continue to have trouble as I work my way down into the nested objects, I'll keep using Logger.log(data["routes"][0]...) to see what the next key/index should be.
It looks like you had a pretty good idea of the hierarchy and you were just missing the array portion of the objects, which is why you were getting the errors. So adding in [0]'s after each key call should fix your problem.
Also, just as a sidenote, you can use dot notation when accessing keys in an object in Google Scripts, i.e. data.routes[0].legs[0].steps[0].polyline (which returns the polyline object).

format json data in javascript like a pivot table

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.

Categories