0

Summary

I receive a large JSON object in node--about 10000 lines--from an external API and I'm creating a new, consolidated javascript object with the data I want.

I'm extracting specific key:value pairs from an object, where another key:value pair in the object matches what I'm looking for. The main issue I'm having is that if there is no data for a specific object, that object is not included and the function I wrote to assign the specific data I want to a variable becomes undefined and crashed my node server.

**Example API Response (Abbreviated) ** I commented on the data I'm trying to extract

{
    "ApiJSONObject": {
        "id": "t4365qewsagsdga4",
        "stats": [{
                "metadata": {
                    "key": "kills",
                    "name": "Kills",
                    "isReversed": false
                },
                "value": 6435, //Extract this value and save to specific key in new javascript object
                "displayValue": "6,435"
            }
        ],
        "segments": [{
            "metadata": [{
                    "key": "segment",
                    "name": "Segment",
                    "value": "br.close_solo.season",
                    "displayValue": null
                },
                {
                    "key": "lastPlayedAt",
                    "name": "Last Played At",
                    "value": "2018-12-11T16:46:35Z",
                    "displayValue": "12/11/18 4:46:35 PM"
                },
                {
                    "key": "updatedAt",
                    "name": "Updated At",
                    "value": "2019-06-10T19:07:00.9143166Z",
                    "displayValue": "6/10/19 7:07:00 PM"
                }
            ],
            "stats": [{
                    "metadata": {
                        "key": "kills",
                        "name": "Kills",
                        "isReversed": false
                    },
                    "value": 1, //extract this value and save to specific key in new javascript object based on metaData[0].value
                    "displayValue": "1"
                },
                {
                    "metadata": {
                        "key": "matchesPlayed",
                        "name": "Matches Played",
                        "isReversed": false
                    },
                    "value": 1,
                    "displayValue": "1"
                }
            ]
        }]
    }
}

Current Function

I wrote this function, however it breaks my code as stats is undefined if there is no data for that specific statsSegment

function getSegmentStats(statType, playerStats) {
  let filteredMetaData = [];
  for (var i = 0; i < playerStats.segments.length; i++) {
    filteredMetaData = playerStats.segments[i].metadata.filter(
      val => val["value"] === statType
    );
    if (filteredMetaData.length) {
      return playerStats.segments[i];
    }
  }
}

function getStatsFields(value,"br.close_solo.season") {
  const stat = statsSegment.stats.find(x => x.metadata.name === value);
  return stat.value;
}

const seasonSolo = getSegmentStats("br.close_solo.season", playerStats);

const statsObject = { seasonStats: seasonSolo: getStatsFields("Kills", seasonSolo))
bzlight
  • 1,066
  • 4
  • 17
  • 43

3 Answers3

3

The simplest solution would be to just check if your statsSegment is undefined at the start of your function, but first you need to decide what you do in case it is undefined. you have few options:

  1. throw an error

  2. return an "errored" value- 0, false, -1- something that will never get returned as stat.value and you'll know for sure it means an error.

  3. emit an event of some sort (don't know the context you're using this).

To perform the check simply add if(statSegment === undefined) at the start of getStatField function. Also, i'd suggest you look at the docs for that 3rd party API you're using and see what undefined return value even means. And one last thing- this API might return an empty object (also, check at the docs), so the undefined test will pass but you still won't be able to process the data. You can add an empty object test as well:

if(statSegment === undefined || (Object.entries(statSegment).length === 0 && statSegment.constructor === Object));

if you're using ECMA 7+, or:

if(statSegment === undefined || (Object.keys(statSegment).length === 0 && statSegment.constructor === Object));

if you're using ECMPA 5+.

(for more info about this empty object check go here)

Gibor
  • 1,695
  • 6
  • 20
  • Thanks for the detailed answer, I really appreciate it! I updated the question since to provide more information. The API is to pull player's stats from a video game and it returns no object for that segment of data if I has no stats for that segment. Therefore I want getStatsFields to return 0 if that object is undefined. Do you think const stat = statsSegment.stats.find(x => x.metadata.name === value) || {value: 0}; is a good approach in that case? – bzlight Aug 04 '19 at 17:47
  • That actually didn't work because statsSegment is undefined and it's trying to read statsSegment.stats. I ended up using const stat = statsSegment ? statsSegment.stats.find(x => x.metadata.name === value) : {value: 0}; – bzlight Aug 04 '19 at 19:16
  • @bzlight sounds like a valid approach after you fixed it :) just be sure that you don't use any other properties of the returning object aside from `value`, or else your code would fail up ahead. If you do, add a default value to it at the returning object as well. – Gibor Aug 05 '19 at 06:55
1

When .find() can't find anything which matches its inner function's criteria, it will by default return undefined. By using the logical OR operator (||) you can set the value of stat to be a default object which always has a value of 0:

function getStatsFields(value,"br.close_solo.season") {
  const stat = statsSegment && statsSegment.stats.find(x => x.metadata.name === value) || {value: 0};
  return stat.value;
}  
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
  • This still came back with an error because statsSegment is undefined so it can't read .stats from it. I used const stat = statsSegment ? statsSegment.stats.find(x => x.metadata.name === value) : { value: 0 }; instead which worked! Thanks for your help though! – bzlight Aug 04 '19 at 18:06
  • @bzlight No worries, I've modified my code and should work now. Using `const stat = statsSegment ? statsSegment.stats.find(x => x.metadata.name === value) : { value: 0 };` code will work if `statsSegment` is undefined, but if `.find()` happens to return `undefined` then you'll get an error (eg: if `value` isn't a valid _name_). – Nick Parsons Aug 05 '19 at 01:13
0

let statsSegmentJson = {
    "ApiJSONObject": {
        "id": "t4365qewsagsdga4",
        "stats": [{
                "metadata": {
                    "key": "kills",
                    "name": "Kills",
                    "isReversed": false
                },
                "value": 6435, //Extract this value and save to specific key in new javascript object
                "displayValue": "6,435"
            }
        ],
        "segments": [{
            "metadata": [{
                    "key": "segment",
                    "name": "Segment",
                    "value": "br.close_solo.season",
                    "displayValue": null
                },
                {
                    "key": "lastPlayedAt",
                    "name": "Last Played At",
                    "value": "2018-12-11T16:46:35Z",
                    "displayValue": "12/11/18 4:46:35 PM"
                },
                {
                    "key": "updatedAt",
                    "name": "Updated At",
                    "value": "2019-06-10T19:07:00.9143166Z",
                    "displayValue": "6/10/19 7:07:00 PM"
                }
            ],
            "stats": [{
                    "metadata": {
                        "key": "kills",
                        "name": "Kills",
                        "isReversed": false
                    },
                    "value": 1, //extract this value and save to specific key in new javascript object based on metaData[0].value
                    "displayValue": "1"
                },
                {
                    "metadata": {
                        "key": "matchesPlayed",
                        "name": "Matches Played",
                        "isReversed": false
                    },
                    "value": 1,
                    "displayValue": "1"
                }
            ]
        }]
    }
};
    let value = 'Kills';
        function getStatsFields(value, statsSegment) {
          let statsSegmentStr = JSON.stringify(statsSegment);
          let statsSegmentObj = JSON.parse(statsSegmentStr);
          console.log("statsSegment", statsSegmentObj);
          console.log("statsSegment.stats ", statsSegmentObj.ApiJSONObject.stats);
          const stat = statsSegmentObj.ApiJSONObject.stats.find(x => x.metadata.name === value);
          if (stat) {
            return stat.value;
          }
        }

    let result = getStatsFields(value,statsSegmentJson);
    console.log(result);
Fayez Ahamed
  • 21
  • 1
  • 4