1

I am building a boat visualizer using AISHub APIs. After inquiring the APIs I am able to obtain a json file with thousands of vessels, but I filter only the vessels I am interested in, and inject them into a table on a webpage. The API gives the following fileds: [NAME, MMSI, LONGITUDE, LATITUDE, others...]. The most important parameters I am using for the filtering are : NAME and MMSI (whereas there could be multiple vessels with same NAME, there cannot be two vessels with the same MMSI number because it is unique).

The problem I have is that the filter function does not seem to have the proper behavior. In fact it does not filter uniquely for that specific NAME and/or MMSI and I end up having multiple vessels with same NAME and with different MMSI. Also the vessel that should appear, it does not, despite I hard-coded the NAME and MMSI for that specific vessel. Which is not explainable as I hard-coded those numbers to be specifically filtered.

Below the code I am using for the filtering search:

var express = require('express');
var router = express.Router();
var axios = require('axios');
const NodeCache = require('node-cache');
const myCache = new NodeCache();

let hitCount = 0;

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', { title: 'Express' });
});

const mmsiOfInterest = [
    '367029520', // ok -> MICHIGAN
    '366909730', // ok -> JP BOISSEAU
    '367128570', // ok -> DELAWARE BAY
    '366744010', // ok -> ATLANTIC SALVOR
    // Other MMSI numbers .....
];


const shipNamesOfInterest = [
    'MICHIGAN',
    'JP BOISSEAU',
    'DELAWARE BAY',
    'ATLANTIC SALVOR',
    // Other NAMES....
]

router.get('/hello', async function(req, res, next) {
    const allData = myCache.get('allData');

    if (!allData) {
        hitCount++;
        console.log(`hit ${hitCount} number of times`);
        const { data } = await axios.get(
            'http://data.aishub.net/ws.php?username=MY_KEY&format=1&output=json&compress=0&latmin=11.42&latmax=58.20&lonmin=-134.09&lonmax=-52.62'
        );

        const [ metaData, ships ] = data;
        console.log(data);

        const shipsOfInterest = ships.filter(
            (ship) => mmsiOfInterest.includes(ship.MMSI) || shipNamesOfInterest.includes(ship.NAME)
        );
        myCache.set('allData', shipsOfInterest, 70);
        res.send(data);
        return;
    }

    console.log('this is the data:', allData);
    res.send(allData);
});

module.exports = router;

Also below a typical JSON response from the API:

{"MMSI":225342000,"TIME":"2020-01-26 01:45:48 GMT","LONGITUDE":1.43912,"LATITUDE":38.91523,"COG":339.9,"SOG":0,"HEADING":297,"ROT":0,"NAVSTAT":0,"IMO":9822449,"NAME":"ILLETAS JET","CALLSIGN":"EAVX","TYPE":40,"A":4,"B":25,"C":4,"D":4,"DRAUGHT":0,"DEST":"IBIZA","ETA":"00-00 00:00"}

What I have done so far:

1) I tried different combination with the filter function and I tried to filter by MMSI, which is supposed to be unique for each vessel, but still I end up with vessels with same NAME and different MMSI (despite I hard-coded the MMSI...I don't understand):

const shipsOfInterest = ships.filter(
                (ship) => mmsiOfInterest.includes(ship.MMSI)
            );

After I tried filtering by NAME, but that does not work either:

const shipsOfInterest = ships.filter(
                (ship) => shipNamesOfInterest.includes(ship.NAME)
            );

EDIT 2

router.get('/hello', async function(req, res, next) {
    //
    const allData = myCache.get('allData');

    if (!allData) {
        hitCount++;
        console.log(`hit ${hitCount} number of times`);
        const { data } = await axios.get(
            'http://data.aishub.net/ws.php?username=KEY&format=1&output=json&compress=0&latmin=11.42&latmax=58.20&lonmin=-134.09&lonmax=-52.62'
        );

        // console.log(data);
        const { metaData, ships } = data;
        const set = new Set();
        const shipsOfInterest = ships.filter((ship) => {
            if (set.has(ship.MMSI)) return false;
            set.add(ship.MMSI);
            return true;
        });
        myCache.set('allData', shipsOfInterest, 70);
        res.send(data);
        return;
    }

    console.log('this is the data:', allData);
    res.send(allData);
});

module.exports = router;

Below the error:

error

I don't know if the filter function can get a better result if organized in a different way. I thought that this could have been a very good way to organize the search and don't understand what it does not work. Thanks for pointing to the right direction for solving this issue.

Emanuele
  • 2,194
  • 6
  • 32
  • 71
  • By "hard-coding the MMSI, you're saying that something like the following code returns ships with multiple MMSI values? `const shipsOfInterest = ships.filter(ship => ship.MMSI === '367029520' );` – Ed Lucas Feb 27 '20 at 15:25
  • Also, can you provide a small example of the JSON that's returned by the API? Their website only shows an XML formatted example. – Ed Lucas Feb 27 '20 at 15:31
  • I didn't try that straight as I hard coded an association map: `mmsiOfInterest` is matching exactly `shipNamesOfInterest`. I could try that though. Also yes let me post a `JSON` format typical example. – Emanuele Feb 27 '20 at 15:40
  • Ok, posted the `JSON` response – Emanuele Feb 27 '20 at 15:50
  • With your new error, you should try wrapping your `get` in a "try/catch" block to stop execution if your API returns an error code. – Ed Lucas Feb 27 '20 at 16:47

4 Answers4

1

I guess you made mistake while using the shorthand assignment. Use {} instead of [].

Instead of:

  const [ metaData, ships ] = data;

try:

  const { metaData, ships } = data;
Flavin
  • 161
  • 1
  • 5
  • Thanks for reading the question. Unfortunately if I switch with your suggestion nothing works, and the function does not even filter any vessels. I though about doing that but never tried until now. – Emanuele Feb 27 '20 at 15:52
  • Ok I updated the question carrying your suggestion too. There is **EDIT 2** with the provided code and a [print screen](https://i.imgur.com/9ffBBv9.png) of the error. Is that useful? – Emanuele Feb 27 '20 at 16:22
1

In the end of if statement you do res.send(data), which is basically the data you received from the API. Instead, you need to res.send(shipsOfInterest); Also, change the format of mmsiOfInterest list from strings to numbers, because you receive numbers from the API.

0

So, as far as I understand you the filtered list to contain the records with unique MMSI value. So, what you need to do is:

const uniqueMMSI = new Set();
const shipsOfInterest = ships.filter(
  (ship) => {
    if (set.has(ship.MMSI)) return false;
    set.add(ship.MMSI);
    return true;
  }
);

I hope I understood your question :)

  • Thanks for reading the question. I tried your solution and it works partially as it starts filtering but it stops throwing: `(node:16461) UnhandledPromiseRejectionWarning: ReferenceError: set is not defined` – Emanuele Feb 27 '20 at 15:37
  • Sorry, I made a typo, please rename `uniqueMMSI` to `set`. – Vyacheslav Palamar Feb 27 '20 at 15:42
  • No worries, do you mean this: `const uniqueMMSI = new set();` – Emanuele Feb 27 '20 at 15:44
  • Ok a small progress. It started filtering but it stopped throwing this error: `(node:17284) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'filter' of undefined`. I will make a print screen of the error. – Emanuele Feb 27 '20 at 15:59
  • @Flavin explained why it is undefined here https://stackoverflow.com/a/60436602/5847921 – Vyacheslav Palamar Feb 27 '20 at 16:10
  • I'd also propose to check the contents of data by `console.log(data)` because I am not sure that your destructuring is correct. – Vyacheslav Palamar Feb 27 '20 at 16:13
  • Ok I updated the question. There is **EDIT 2** with the provided suggestion and a [print screen](https://i.imgur.com/9ffBBv9.png) of the error. Is that useful? – Emanuele Feb 27 '20 at 16:20
  • You have this error because there is no `ships` field in `data`. Can you `console.log(data)` and tell me what is the result? – Vyacheslav Palamar Feb 27 '20 at 16:45
  • Yes, ok [this is the printscreen](https://i.imgur.com/gkJxG5H.png) of the `console.log(data + 'ERROR IS:');` – Emanuele Feb 27 '20 at 17:02
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/208657/discussion-between-vyacheslav-palamar-and-emanuele). – Vyacheslav Palamar Feb 27 '20 at 17:04
0

It looks like MMSI is coming in as a numeric value, while your comparison array is holding strings. How about using an array of integers for your comparison?

const mmsiOfInterest = [
    367029520,
    366909730, 
    367128570, 
    366744010, 
    // Other MMSI numbers .....
];
Ed Lucas
  • 5,955
  • 4
  • 30
  • 42
  • That was another option I didn't try. I just tried it and it started filtering but stopped with the error: `(node:17733) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'filter' of undefined` – Emanuele Feb 27 '20 at 16:10
  • Ok I updated the question. There is **EDIT 2** with the provided suggestion and a [print screen](https://i.imgur.com/9ffBBv9.png) of the error. Is that useful? – Emanuele Feb 27 '20 at 16:22