0

I am getting a JSON file which contains weather data about a number of cities. Here is an example of the data.

const weatherArray = [{
            "Date": '5-1-19',
            "Location": 'New York',
            "Rainfall": 4},
                                     {
            "Date": '5-1-19',
            "Location": 'Miami',
            "Rainfall": 8},
                                     {
            "Date": '5-1-20',
            "Location": 'New York',
            "Rainfall": 9},
                                     {
            "Date": '5-1-20',
            "Location": 'Miami',
            "Rainfall": 2},
                                     {
            "Date": '5-1-21',
            "Location": 'New York',
            "Rainfall": 10},
                                     {
            "Date": '5-1-21',
            "Location": 'Chicago',
            "Rainfall": 9},
                                    ]

What I need to do is filter this data and store the maximum rainfall for each city in an array. I believe my function is close but filterData is returning an array of 6 unknown objects.

filterData = (inputArray) => {
    let rain = inputArray.map(obj => rain.find(o => o.Location === obj.Location && obj.Rainfall > o.Rainfall) || rain.find(o => o.Location !== obj.Location));
    return rain;
    }

I would like the output array to contain the entire object associated with the maximum rainfall for each city in the JSON file.

rain = [{
        "Date": '5-1-19',
        "Location": 'Miami',
        "Rainfall": 8},
{
        "Date": '5-1-21',
        "Location": 'New York',
        "Rainfall": 10},
{
        "Date": '5-1-21',
        "Location": 'Chicago',
        "Rainfall": 9},
]
2
  • 1
    please add the wanted result. Commented Mar 30, 2019 at 22:27
  • do you have different dates? Commented Mar 30, 2019 at 22:40

5 Answers 5

2

You could take a map, collect the max rainfall of a location and get a new array of object with the result.

const
    weatherArray = [{ Date: "5-1-19", Location: "New York", Rainfall: 4 }, { Date: "5-1-19", Location: "Miami", Rainfall: 8 }, { Date: "5-1-20", Location: "New York", Rainfall: 9 }, { Date: "5-1-20", Location: "Miami", Rainfall: 2 }, { Date: "5-1-21", Location: "New York", Rainfall: 10 }, { Date: "5-1-21", Location: "Chicago", Rainfall: 9 }],
    filterData = (inputArray) => {
        return Array.from(
            inputArray.reduce((m, { Location, Rainfall }) =>
                m.set(Location, Math.max(m.get(Location) || 0, Rainfall)), new Map),
            ([Location, Rainfall]) => ({ Location, Rainfall })
        );
    };
  
console.log(filterData(weatherArray));
.as-console-wrapper { max-height: 100% !important; top: 0; }

With date.

const
    weatherArray = [{ Date: "5-1-19", Location: "New York", Rainfall: 4 }, { Date: "5-1-19", Location: "Miami", Rainfall: 8 }, { Date: "5-1-20", Location: "New York", Rainfall: 9 }, { Date: "5-1-20", Location: "Miami", Rainfall: 2 }, { Date: "5-1-21", Location: "New York", Rainfall: 10 }, { Date: "5-1-21", Location: "Chicago", Rainfall: 9 }],
    filterData = (inputArray) => {
        return Array.from(inputArray
            .reduce((m, o) => {
                var temp = m.get(o.Location)
                return temp && temp.Rainfall > o.Rainfall
                    ? m
                    : m.set(o.Location, o);
            }, new Map)
            .values()
        );
    };
  
console.log(filterData(weatherArray));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

3 Comments

nice but this solution doesn't preserve the date of the last rainfall taken
@jsdeveloper, is that wanted?
maybe not but it was in the wanted result posted above
1

Your function is far from being usable :) Try simple reduce instead:

weatherArray.reduce((accumulator, current) => {
    // check if given city is already in reducing array result
    let cityExists = accumulator.findIndex(k => k.Location === current.Location)
    if(cityExists > -1) {
        // there is a city in resulting array, check values and perhaps update
        if(accumulator[cityExists].Rainfall < current.Rainfall) {
            accumulator[cityExists].Rainfall = current.Rainfall
            accumulator[cityExists].Date = current.Date
        }
    } else {
        // no such city, just add it
        accumulator.push(current)
    }
    return accumulator
}, []) // start with an empty array

Comments

0

Personally, I would break down the task into several sub tasks (instead of trying to do it all in one line), like so:

task 1: get all the unique locations

task 2: for a specific location, find the max rainfall

task 3: combine the unique locations with their respective max rainfalls

If you write a function for each one of these, I believe you should be able to solve it on your own.

Comments

0

Just use reduce:

const weatherArray = [{
    "Date": '5-1-19',
    "Location": 'New York',
    "Rainfall": 4
  },
  {
    "Date": '5-1-19',
    "Location": 'Miami',
    "Rainfall": 8
  },
  {
    "Date": '5-1-20',
    "Location": 'New York',
    "Rainfall": 9
  },
  {
    "Date": '5-1-20',
    "Location": 'Miami',
    "Rainfall": 2
  },
  {
    "Date": '5-1-21',
    "Location": 'New York',
    "Rainfall": 10
  },
  {
    "Date": '5-1-21',
    "Location": 'Chicago',
    "Rainfall": 9
  }
];

const rain = weatherArray.reduce((acc, { Date, Location, Rainfall }) => {
  if (acc.some(e => e.Location == Location)) { 
    if (acc.find(e => e.Location == Location).Rainfall > Rainfall) {
      return acc;
    } 
    acc.push({ Date, Location, Rainfall});
    acc.splice(acc.findIndex(e => e.Location == Location), 1);
    return acc;
  }
  acc.push({ Date, Location, Rainfall });
  return acc;
}, []);

console.log(rain);
.as-console-wrapper { max-height: 100% !important; top: auto; }

Comments

0

What I would advise you is to start splitting your functional programming into more understandable functions (for you) before trying to create one-liners.

There are multiple algorithms you can use for your solution:

  • You could get an array of results for each city, sort it and get the biggest one (this can probably be cleaner by using Array.reduce). Very close to @Maciej Kwas solution.
  • You could create an object to use as a dictionary where, when you find a city for the first time, you store its name and its rainfall value and for each element in the iteration, check if the city is already in your object and update the rainfall value if it's bigger.
  • You can use recursion to group your values per city, then iterate over them.

In my opinion, I believe that you should write readable code first, then worry about performance, unless your software requirements demand performance-first.

Therefore option 2 would be my recommended solution and this could be the code:

const rain = {};
weatherArray.forEach(weather => {
    const currentCity = weather.Location;
    if (rain.hasOwnProperty(currentCity) && rain[currentCity].Rainfall < weather.Rainfall) {
        rain[currentCity] = {Rainfall: weather.Rainfall, Date: weather.Date };
    } else {
        rain[currentCity] = weather;
    };)

rain will be an object of values:

{
'Miami': {"Date": '5-1-19',
        "Rainfall": 8},
'New York': {
        "Date": '5-1-21',
        "Rainfall": 10},
'Chicago': {
        "Date": '5-1-21',
        "Rainfall": 9},
}

Can be easily converted into an array if that's your final result. If ES2016 is available you can also use the new Map iterables.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.