1

Apologies if the question is worded weird, but it is much easier to explain with an example.

Suppose I have a json containing

[
    {
      "UID": 84001001,
      "iso2": "US",
      "iso3": "USA",
      "code3": 840,
      "FIPS": 1001,
      "Admin2": "Autauga",
      "Province_State": "Alabama",
      "Country_Region": "US",
      "Lat": 32.53952745,
      "Long_": -86.64408227,
      "Combined_Key": "Autauga, Alabama, US",
      "1/22/20": 0,
      ... so on and so on ...
      "5/27/21": 21606
    },
    {
      "UID": 84001003,
      "iso2": "US",
      "iso3": "USA",
      "code3": 840,
      "FIPS": 1003,
      "Admin2": "Baldwin",
      "Province_State": "Alabama",
      "Country_Region": "US",
      "Lat": 30.72774991,
      "Long_": -87.72207058,
      "Combined_Key": "Baldwin, Alabama, US",
      "1/22/20": 0,
      ... so on and so on ...
      "5/27/21": 21606
    },
...
]

and this json continues for all counties for all US states.

I have two react state variables, USState and county. If we are iterating through every currState in the json and USState === currState.Province_State and county === currState.Admin2, then I want that object.

Example: if USState = "Alabama" and county = "Baldwin", then this object will contain:

dataObject = {
      "UID": 84001003,
      "iso2": "US",
      "iso3": "USA",
      "code3": 840,
      "FIPS": 1003,
      "Admin2": "Baldwin",
      "Province_State": "Alabama",
      "Country_Region": "US",
      "Lat": 30.72774991,
      "Long_": -87.72207058,
      "Combined_Key": "Baldwin, Alabama, US",
      "1/22/20": 0,
      ... so on and so on ...
      "5/27/21": 21606
    }

I already have this part done. I have a dataObject like above. I do not want to include my code for that because that's more related to React and this question is geared more towards JavaScript and json.

Now, I want to filter through this object to only have the dates, so now dataObject will contain

dataObject = {
      "1/22/20": 0,
      ... so on and so on ...
      "5/27/21": 21606
    }

then finally, I want to construct an array based on these key value pairs to get

array = [ 
            { x : new Date(2020, 01, 22), y : 0}, 
            ..., 
            { x : new Date(2021, 05, 27), y : 21606}
        ]

so I can plot it using Canvas.js.

However, as a beginner with react, I find myself getting confused and roadblocked, constantly using map and filter functions while also getting used to this style of programming. I think I'm making this way harder or more complicated than it needs to be. This is also my first time dealing with json.

Basically, I just want to filter my dataObject to only contain keys with dates, then plot these dates (keys) vs their number (value). I would appreciate any help. Thank you

zzzz
  • 67
  • 8
  • JSON is a *textual notation* for data exchange. [(More here.)](http://stackoverflow.com/a/2904181/157247) If you're dealing with JavaScript source code, and not dealing with a *string*, you're not dealing with JSON. – T.J. Crowder May 30 '21 at 12:06
  • @T.J.Crowder i see. this was originally a ```.csv``` file but i converted it to ```.json``` because i thought it'd make things easier. rookie mistake :/ – zzzz May 30 '21 at 12:11

1 Answers1

1

You can do that by using filter and map as you've described, starting out by converting the object to an array of [key, value] pairs via Object.entries, see comments:

// A regular expression for your date format
const rexDate = /^(?<month>\d{1,2})\/(?<day>\d{1,2})\/(?<year>\d{2})$/;
// 1. Convert the object to a [key, value] array
// 2. Filter out the properties whose keys don't match the regex
// 3. Map the surviving properties to the object you want
const array = Object.entries(dataObject)
    .filter(([key]) => rexDate.test(key)) // Is it a date according to our regex?
    .map(([key, value]) => {
        // Get the capture group using their names
        const {groups: {month, day, year}} = rexDate.exec(key);
        // Build the object
        return {
            x: new Date(Number(year) + 2000, Number(month) - 1, Number(day)),
            y: value
        };
    });

Live Example:

const dataObject = {
    "UID": 84001003,
    "iso2": "US",
    "iso3": "USA",
    "code3": 840,
    "FIPS": 1003,
    "Admin2": "Baldwin",
    "Province_State": "Alabama",
    "Country_Region": "US",
    "Lat": 30.72774991,
    "Long_": -87.72207058,
    "Combined_Key": "Baldwin, Alabama, US",
    "1/22/20": 0,
    // ... so on and so on ...
    "5/27/21": 21606
};

const rexDate = /^(?<month>\d{1,2})\/(?<day>\d{1,2})\/(?<year>\d{2})$/;
const array = Object.entries(dataObject)
    .filter(([key]) => rexDate.test(key)) // Is it a date according to our regex?
    .map(([key, value]) => {
        // Get the capture group using their names
        const {groups: {month, day, year}} = rexDate.exec(key);
        // Build the object
        return {
            x: new Date(Number(year) + 2000, Number(month - 1), Number(day)),
            y: value
        };
    });
console.log(array);

(The snippet console shows the dates as strings, but they're Date instances in the actual objects.)

I've used named capture groups there, which are fairly new but well-supported in modern browsers, but you could use anonymous ones instead.

I've also used Number to convert from string to number, but I cover various other options in this answer.

That version ends up executing the regular expression twice for each property. You can avoid that by taking another pass through the data, but it's probably not worth it.

You could also do this with a for-in loop, all in a single pass:

const rexDate = /^(?<month>\d{1,2})\/(?<day>\d{1,2})\/(?<year>\d{2})$/;
const array = [];
// This strange-looking thing creates a function that you call with an object and
// a key to see if the object has an "own" property with that key.
const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
for (const key in dataObject) {
    // If this is an "own" property of the object, try to match it with the
    // regular expression; bail if it's not an own property or doesn't match
    let match;
    if (!hasOwn(dataObject, key) || !(match = rexDate.exec(key))) {
        continue;
    }
    // Get the capture group values using their names
    const {groups: {month, day, year}} = match;
    // Build the object and add it to the array
    array.push({
        x: new Date(Number(year) + 2000, Number(month - 1), Number(day)),
        y: dataObject[key]
    });
}

Live Example:

const dataObject = {
    "UID": 84001003,
    "iso2": "US",
    "iso3": "USA",
    "code3": 840,
    "FIPS": 1003,
    "Admin2": "Baldwin",
    "Province_State": "Alabama",
    "Country_Region": "US",
    "Lat": 30.72774991,
    "Long_": -87.72207058,
    "Combined_Key": "Baldwin, Alabama, US",
    "1/22/20": 0,
    // ... so on and so on ...
    "5/27/21": 21606
};

const rexDate = /^(?<month>\d{1,2})\/(?<day>\d{1,2})\/(?<year>\d{2})$/;
const array = [];
// This strange-looking thing creates a function that you call with an object and
// a key to see if the object has an "own" property with that key.
const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
for (const key in dataObject) {
    // If this is an "own" property of the object, try to match it with the
    // regular expression; bail if it's not an own property or doesn't match
    let match;
    if (!hasOwn(dataObject, key) || !(match = rexDate.exec(key))) {
        continue;
    }
    // Get the capture group values using their names
    const {groups: {month, day, year}} = match;
    // Build the object and add it to the array
    array.push({
        x: new Date(Number(year) + 2000, Number(month - 1), Number(day)),
        y: dataObject[key]
    });
}
console.log(array);

Side note: Your dates appear to use two-digit years. I'd be very leery of using two-digit year values. :-)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875