user6700001
user6700001

Reputation:

Iterate over 2 nested lists

I am writing a weather app and need to go through 2 nested loops. For the return value I want to iterate over the first list, looking at the corresponding second list data. When the data in the second list matches the bool, I need to get data from the corresponding first list. Now I think that my code works... but would like to ask if this is a good way to do this. I am also not sure if this LINQ query will work in general, with even more nested lists. Here's my approach in LINQ:

public static async Task<string> UpdateWeather(string lat, string lon)
{
    WeatherObject weather = await WeatherAPI.GetWeatherAsync(lat, lon);

    var first = (from l in weather.list
                from w in l.weather
                where w.id == 800
                select l.city.name).First();

    return first;
}

Upvotes: 0

Views: 540

Answers (2)

Chris Simon
Chris Simon

Reputation: 6505

I believe your query should work, and it should generally work with more nested lists following a similar structure. As to if it is a good way to do this - it depends on the data structure and any data constraints.

For example, if two elements in weather.list have an element in their nested weather list might have the same id, then your code will only return the first one - which may not be correct.

e.g. in json:

[
  {
    city : {
      name : "Chicago"
    },
    weather : [
      {
        id = 799
      },
      {
        id = 800
      }
    ]
  },
  {
    city : {
      name : "New York"
    },
    weather : [
      {
        id = 800
      },
      {
        id = 801
      }
    ]
  }
}

For this dataset, your code will return "Chicago", but "New York" also matches. This may not be possible with the data API you are accessing, but given that there are no data constraints to ensure exclusivity of the nested lists, you might want to defensively check that there is only 0 or 1 elements in the returned list that match the expected criteria.

Another suggestion

On another note, not strictly an answer to your question - if you think your code will work but aren't sure, write a unit test. In this case, you'd wrap the call to WeatherAPI in a class that implements an interface you define. Update your method to call the method on a reference to the interface.

For your real application, ensure that an instance of the wrapper/proxy class is set on the reference.

For the unit test, use a framework like Moq to create a mock implementation of the interface that returns a known set of data and use that instead. You can then define a suite of unit tests that use mocks that return different data structures and ensure your code works under all expected structures.

This will be a lot easier if your class is not a static method as well, and if you can use dependency injection (Ninject, Autofac or one of many others...) to manage injecting the appropriate implementation of the service.

Further explanations of unit testing, dependency injection and mocking will take more than I can write in this answer, but I recommend reading up on it - you'll never find yourself thinking "I think this code works" again!

Upvotes: 0

Suren Srapyan
Suren Srapyan

Reputation: 68635

Your code is OK, it is a LINQ query.But one more thing. Use FirstOrDefault() instead of First(). First() will throw an exception if no matched element is found, but FirstOrDefault() will return the element or the default value.

You can also write in LINQ Method syntax if you prefer this.

public static async Task<string> UpdateWeather(string lat, string lon)
{
    WeatherObject weather = await WeatherAPI.GetWeatherAsync(lat, lon);

    var first = weather.list.Where(l => l.weather.Any(w => w.id == 800))
                            .Select(l => l.city.name)
                            .FirstOrDefault();

    return first;
}

Upvotes: 2

Related Questions