0

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>VR train search</title>
<!-- 
Data provided by https://www.digitraffic.fi/, used under the following license:
Creative Commons Attribution 4.0 International (CC BY 4.0)

Search for train connections through the VR API
https://www.digitraffic.fi/rautatieliikenne/#reittiperusteinen-haku

 -->
</head>

<body>
 <h3>Train search</h3>

 <form>
  <p>Ignore the search fields, I have hard-coded the stations in this example to make the code shorter. Just click Search trains.</p>
  <label>From:</label> 
  <input type="text" id="departure_station">
  <label>To:</label> 
  <input type="text" id="arrival_station">
  <label>Date (yyyy-mm-dd):</label> 
  <input type="text" id="departure_date">
  <input type="button" value="Search trains" onclick="searchTrains()">
 </form>

 <div id="onscreen_text"></div>

 <script type="text/javascript">

 function searchTrains() {

  var departure_station = "HKI";
  var arrival_station = "TPE";
  var departure_date = "2019-02-12";
 
  var xmlhttp = new XMLHttpRequest();
  var json;
   
  xmlhttp.onreadystatechange = function() {

   if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
    json = xmlhttp.responseText;
    listResults(json);
   }
   
   if (xmlhttp.readyState == 4 && xmlhttp.status == 404) {
    document.getElementById("onscreen_text").innerHTML = "<p>No search results found in the VR API.</p>";
   }
  }
  // Get the data from the API
  xmlhttp.open(
   "GET", "https://rata.digitraffic.fi/api/v1/live-trains/station/" + departure_station + "/" + arrival_station + "?" + "departure_date=" + departure_date + "&limit=10", true);
  xmlhttp.send();
 }
 
 function listResults(json) {

  var results = JSON.parse(json);
  console.log(results); // testing
  var text = ""; // testing

  // filter results for only passenger trains
  var passengerTrains = [];
  function getPassengerTrains() {
   for (var i = 0; i < results.length; i++) {
    if (results[i].trainCategory == "Long-distance" || results[i].trainCategory == "Commuter") {
     passengerTrains.push(results[i]);
     text = text + results[i].trainNumber + "<br>"; // testing
    }
   }
  }
  getPassengerTrains();
  console.log(passengerTrains); // testing
  
  // Get the desired properties from the filtered trains
  // https://stackoverflow.com/questions/37750309/find-object-by-property-in-an-array-of-javascript-objects-inside-another-array
  
  var station = passengerTrains.map(item => item.timeTableRows.stationShortCode);
  console.log(station); // but this returns an array full of undefined
  
  // Loop through array of train objects
  for (var i = 0; i < passengerTrains.length; i++) {
   console.log(passengerTrains[i]); // this seems to give the right result
   // Loop through each train object to get timeTableRows subarray
   for (var train in passengerTrains[i]) {
    // make a new array named timetable
    var timetable = passengerTrains[i].timeTableRows;
    console.log(timetable);
    // but this prints the same array 13 times = as many times as there are properties in the train object
    /* Commented out because it freezes my browser 
    // Loop through each object in the timetable subarray to get the desired properties
    for (var j=0; j < timetable.length; j++) {
     console.log(timetable[j]);
     for (var row in timetable[j]) {
      text = text + "<p>From/to: " + timetable[j].stationShortCode;
      text = text + "<br>Time: " + timetable[j].scheduledTime;
      text = text + "<br>Train stopping: " + timetable[j].trainStopping;
      text = text + "<br>Departure/arrival: " + timetable[j].type + "</p>"; 
     } 
    } */
   } 
  document.getElementById("onscreen_text").innerHTML = text;
  }
 }

 </script>
</body>
</html>

I'm fairly new at programming and this is my first question here. I'm stuck trying to figure out how to get to the bottom level of a multilevel nested array/object thing in JavaScript.

What I'm working with is a parsed JSON file of train schedules of the type returned by this public API: https://rata.digitraffic.fi/api/v1/live-trains/station/HKI/TPE?departure_date=2019-02-12

The parsed result is an array of train objects with a subarray named timeTableRows full of objects with properties such as stationShortCode: "HKI", scheduledTime: "2019-02-12T05:04:00.000Z", etc. I want to get to these bottom-level properties. Screenshot from console for clarity

I tried the answer given here: Find object by property in an array of JavaScript objects inside another array

But I can't figure out how to make it work for me, since I don't need to find a certain index but only a property by a certain name, and my attempts only return "undefined". I'm not at all familiar with ES5, so I would appreciate some beginner-friendly examples.

I commented out the rest of the for loops, since they created an iterating monstrosity that froze my browser. Is there any way to get to the bottom level of this kind of structure without looping multiple times through everything?

js snippet removed
ssilvonen
  • 33
  • 1
  • 6
  • you should make use of a recursive method which will loop through the whole object as long as it finds further children – messerbill Feb 11 '19 at 15:10
  • Please [edit] your question to include an example of the data you are looking for. Not everyone can access the site if they're behind firewalls, etc. You could include it in your snippet so that clicking "Run code snippet" did something other than throw an error :). – Heretic Monkey Feb 11 '19 at 15:24
  • Thanks, I edited the example into a full HTML file so that it runs and produces the data in the console. For some reason it runs really slowly here though, but there's no problem if I run it straight from Notepad into Firefox. The screenshot in my post also shows the array structure, with the data that I'm looking for at the bottom level (stationShortCode etc.). – ssilvonen Feb 12 '19 at 08:18

3 Answers3

1
function fetchNestedObject(prop, obj) {
    let keys = prop.split('.');
    keys[0] = obj[keys[0]];
    return keys.reduce((a, b) => a && a[b]);
}

Usage:

fetchNestedObject('v.a.d.0', {v: {a: {d: [1]}}})
varun agarwal
  • 1,491
  • 6
  • 9
0

You could use Ramda for this ,https://ramdajs.com/docs/#path. This will give you the value to your desired property, by typing the path, so you could use it as follow:

R.map(train => {
    train_id:train.trainNumber,
    timeTableRows:R.path(["path","to","property"],train)
}, trains)
Kleppa
  • 11
  • 4
0

Thank you for the answers above, I will note them for later use, although they are too advanced for my current level of JS knowledge. Right now I managed to create a series of nesting for loops that seems to do what I want it to do, but I will be happy to receive further comments or improvements on this solution.

To summarize, the problem was extracting data four levels down from the properties of an object inside an array, which is inside another object inside the top-level array (a parsed JSON response).

        // passengerTrains is the top-level array
        for (var i = 0; i < passengerTrains.length; i++) {
            var train = passengerTrains[i]; // each train is an object
            //console.log(train);
            var trainNumber = train.trainNumber;
            //console.log(trainNumber);
            // Loop through array of timeTableRows inside each train object
            for (var j = 0; j < train.timeTableRows.length; j++) {
                var row = train.timeTableRows[j]; // each row is an object
                // console.log(row);
                // Print out only the data that we want
                // The variables dep and arr are defined elsewhere in the code
                if (row.stationShortCode == dep && row.type == "DEPARTURE") {
                    text = text + "Train " + trainNumber + ": Departure from " + departure_station + " at " + row.scheduledTime + "<br>";
                    console.log("Train " + trainNumber + ": Departure from " + departure_station + " at " + row.scheduledTime);
                }
                if (row.stationShortCode == arr && row.type == "ARRIVAL") {
                    text = text + "Train " + trainNumber + ": Arrival at " + arrival_station + " at " + row.scheduledTime + "<br><br>";
                    console.log("Train " + trainNumber + ": Arrival at " + arrival_station + " at " + row.scheduledTime);
                }
            }
        document.getElementById("onscreen_text").innerHTML = text;
        }
ssilvonen
  • 33
  • 1
  • 6