atalayasa
atalayasa

Reputation: 3480

How to filter two dimensional array

I am trying to build an weather forecast application using Open Weather API and in my forecast class I have following struct type.

struct ForecastWeather {
    public private(set) var date: String    
    public private(set) var weatherDetails: [WeatherInfo]
}

struct WeatherInfo {
    public private(set) var weatherImage: String
    public private(set) var time: String
    public private(set) var weatherType: String
    public private(set) var temperature: String
}

I need to use two dimensional array because in my table view there are sections for forecast datas. I am putting objects into var forecastWeathers = [ForecastWeather]() array.

ForecastWeather(date: "Thursday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "01d", time: "12:00", weatherType: "Clear", temperature: "35.0")])
ForecastWeather(date: "Thursday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "02d", time: "15:00", weatherType: "Clouds", temperature: "31.0")])
ForecastWeather(date: "Thursday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "01n", time: "18:00", weatherType: "Clear", temperature: "21.0")])
ForecastWeather(date: "Friday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "03n", time: "21:00", weatherType: "Clouds", temperature: "16.0")])
ForecastWeather(date: "Friday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "10n", time: "00:00", weatherType: "Rain", temperature: "13.0")])
ForecastWeather(date: "Friday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "10d", time: "03:00", weatherType: "Rain", temperature: "14.0")])
ForecastWeather(date: "Monday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "10d", time: "12:00", weatherType: "Rain", temperature: "19.0")])
ForecastWeather(date: "Monday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "10d", time: "15:00", weatherType: "Rain", temperature: "19.0")])
ForecastWeather(date: "Monday", weatherDetails: [WeatherForecast.WeatherInfo(weatherImage: "03n", time: "18:00", weatherType: "Clouds", temperature: "16.0")])

In my table view and I need to give that sections into it like WeatherService.instance.forecastWeathers[indexPath.section].weatherDetails[indexPath.row]. It is now giving me an error because I have 3 same Thursday object inside it. How can I map or filter forecastWeathers array which contains date information only once because I have multiple sections I have tried to use map function but I could not be successful.

Edit here is my API call

        let json = JSON(value)

        for i in 0..<json["list"].count {
            let icon = json["list"][i]["weather"][0]["icon"].stringValue    // Image icon
            let  date = self.convertUnixDate(fromDoubleTo: json["list"][i]["dt"].doubleValue)
            let weatherType = json["list"][i]["weather"][0]["main"].stringValue.capitalized
            let temperature = "\(self.temperatureConverter(kelvinTo: json["list"][i]["main"]["temp"].doubleValue))"
            let time = self.convertTime(timeArr: json["list"][i]["dt_txt"].stringValue)

            let forecastObj = ForecastWeather(date: date, weatherDetails: [WeatherInfo(weatherImage: icon, time: time, weatherType: weatherType, temperature: temperature)])
            print(forecastObj)
            self.forecastWeathers.append(forecastObj)
        }

Also there is API result 5 day forecast

Here is my table view delegate methods

func numberOfSections(in tableView: UITableView) -> Int {
    return WeatherService.instance.newForecastWeathers.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return WeatherService.instance.newForecastWeathers[section].date.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = forecastTableView.dequeueReusableCell(withIdentifier: cellId) as? ForecastCell else { return ForecastCell() }   
    cell.configureCell(weatherDetails: WeatherService.instance.newForecastWeathers[indexPath.section].weatherDetails[indexPath.row])
    return cell
}
 func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let view = UIView()
    view.backgroundColor = .orange
    let label = UILabel()
    label.text = WeatherService.instance.newForecastWeathers[section].date
    label.frame = CGRect(x: 45, y: 5, width: 100, height: 35)
    view.addSubview(label)
    return view
}

Upvotes: 0

Views: 169

Answers (2)

Vyacheslav
Vyacheslav

Reputation: 27211

I've fixed some errors and allow weatherDetails to be public to simplify appending. This is a functional-style. With sorting.

//: Playground - noun: a place where people can play

struct ForecastWeather {
    public private(set) var date: String
    public var weatherDetails: [WeatherInfo]

    public var index: Int {
        return ForecastWeather.weekdays[date] ?? 0
    }

    private static let weekdays: [String: Int] = ["Sunday": 0, "Monday": 1, "Thuesday": 2, "Wednesday": 3, "Thursday": 4, "Friday": 5, "Saturday": 6]
}

struct WeatherInfo {
    public private(set) var weatherImage: String
    public private(set) var time: String
    public private(set) var weatherType: String
    public private(set) var temperature: String
}

var forecastWeathers = [ForecastWeather]()
forecastWeathers.append(ForecastWeather(date: "Thursday", weatherDetails: [WeatherInfo(weatherImage: "01d", time: "12:00", weatherType: "Clear", temperature: "35.0")]))
forecastWeathers.append(ForecastWeather(date: "Thursday", weatherDetails: [WeatherInfo(weatherImage: "02d", time: "15:00", weatherType: "Clouds", temperature: "31.0")]))
forecastWeathers.append(ForecastWeather(date: "Thursday", weatherDetails: [WeatherInfo(weatherImage: "01n", time: "18:00", weatherType: "Clear", temperature: "21.0")]))
forecastWeathers.append(ForecastWeather(date: "Friday", weatherDetails: [WeatherInfo(weatherImage: "03n", time: "21:00", weatherType: "Clouds", temperature: "16.0")]))
forecastWeathers.append(ForecastWeather(date: "Friday", weatherDetails: [WeatherInfo(weatherImage: "10n", time: "00:00", weatherType: "Rain", temperature: "13.0")]))
forecastWeathers.append(ForecastWeather(date: "Friday", weatherDetails: [WeatherInfo(weatherImage: "10d", time: "03:00", weatherType: "Rain", temperature: "14.0")]))
forecastWeathers.append(ForecastWeather(date: "Monday", weatherDetails: [WeatherInfo(weatherImage: "10d", time: "12:00", weatherType: "Rain", temperature: "19.0")]))
forecastWeathers.append(ForecastWeather(date: "Monday", weatherDetails: [WeatherInfo(weatherImage: "10d", time: "15:00", weatherType: "Rain", temperature: "19.0")]))
forecastWeathers.append(ForecastWeather(date: "Monday", weatherDetails: [WeatherInfo(weatherImage: "03n", time: "18:00", weatherType: "Clouds", temperature: "16.0")]))

var storedForecastWeatherIndicies: [String: ForecastWeather] = [:]

forecastWeathers.forEach { value in
    guard storedForecastWeatherIndicies[value.date] == nil else {
        // if is already found
        storedForecastWeatherIndicies[value.date]?
            .weatherDetails
            .append(contentsOf: value.weatherDetails)
        return
    }
    // if a new value for the Dictinary
    storedForecastWeatherIndicies[value.date] = value
}

let result = storedForecastWeatherIndicies.values.map { $0 }.sorted { first, second in first.index < second.index }

print(result)

[__lldb_expr_7.ForecastWeather(date: "Monday", weatherDetails: [__lldb_expr_7.WeatherInfo(weatherImage: "10d", time: "12:00", weatherType: "Rain", temperature: "19.0"), __lldb_expr_7.WeatherInfo(weatherImage: "10d", time: "15:00", weatherType: "Rain", temperature: "19.0"), __lldb_expr_7.WeatherInfo(weatherImage: "03n", time: "18:00", weatherType: "Clouds", temperature: "16.0")]), __lldb_expr_7.ForecastWeather(date: "Thursday", weatherDetails: [__lldb_expr_7.WeatherInfo(weatherImage: "01d", time: "12:00", weatherType: "Clear", temperature: "35.0"), __lldb_expr_7.WeatherInfo(weatherImage: "02d", time: "15:00", weatherType: "Clouds", temperature: "31.0"), __lldb_expr_7.WeatherInfo(weatherImage: "01n", time: "18:00", weatherType: "Clear", temperature: "21.0")]), __lldb_expr_7.ForecastWeather(date: "Friday", weatherDetails: [__lldb_expr_7.WeatherInfo(weatherImage: "03n", time: "21:00", weatherType: "Clouds", temperature: "16.0"), __lldb_expr_7.WeatherInfo(weatherImage: "10n", time: "00:00", weatherType: "Rain", temperature: "13.0"), __lldb_expr_7.WeatherInfo(weatherImage: "10d", time: "03:00", weatherType: "Rain", temperature: "14.0")])]

EDIT

This is incorrect: date.count is lenght of your string. not the array

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return WeatherService.instance.newForecastWeathers[section].date.count
}

use weatherDetails instead

   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return WeatherService.instance.newForecastWeathers[section].weatherDetails.count
    }

Upvotes: 1

Andreas Oetjen
Andreas Oetjen

Reputation: 10199

The issue is that you are not using the inner weatherDetails as an array, since you always only add just one element. So, you'll have to add WeatherInfos belonging to the same day to that array, maybe in this way:

struct ForecastWeather {
    public private(set) var date: String    
    public private(set) var weatherDetails = [WeatherInfo]()
    public init (date:String) {
      self.date = date
    }
    public mutating func addInfo(_ info:WeatherInfo) {
        weatherDetails.append(info)
    }
}

struct WeatherInfo {
    public private(set) var weatherImage: String
    public private(set) var time: String
    public private(set) var weatherType: String
    public private(set) var temperature: String
}

var thursday = ForecastWeather(date: "Thursday")
thursday.addInfo(WeatherInfo(weatherImage: "01d", time: "12:00", weatherType: "Clear", temperature: "35.0"))
thursday.addInfo(WeatherInfo(weatherImage: "02d", time: "15:00", weatherType: "Clouds", temperature: "31.0"))

var friday = ForecastWeather(date: "Friday")
friday.addInfo(WeatherInfo(weatherImage: "03n", time: "21:00", weatherType: "Clouds", temperature: "16.0"))
// ...

let forecastWeathers = [thursday, friday /* ... */]

Upvotes: 1

Related Questions