Home > Back-end >  Displaying days from dates without repeats
Displaying days from dates without repeats

Time:01-08

I'm having trouble displaying the day from a date. I have multiple dates in the format "2022-12-27T04:00:31.980". I want the "Picker" to be able to select the date after the day of the month. The problem arises when I have the same date but different time, because "Picker" shows me the day as many times as it appears with a different time.

This is my "Picker" code:

Picker(selection: $currentTab, label: Text("Wybierz dzień")) {
                    
                    let calendar = Calendar.current
                    ForEach(temperatureCall.chartDataEntries) { item in
                        let components = calendar.dateComponents([.day, .month, .year], from: item.lastupdated)
                        let day = components.day!
                        let month = components.month!
                        let year = components.year!
                        Text("\(String(day)).\(String(month)).\(String(year))")
                            .tag(String(day))
                    }
                }

enter image description here

I would like each day to be unique and to be able to choose a day no matter how many times it is repeated with a different time.

let calendar = Calendar.current
let dates = Set(temperatureCall.chartDataEntries.map { calendar.startOfDay(for: $0.lastupdated) })

                Picker(selection: $currentTab, label: Text("Wybierz dzień")) {
                    ForEach(dates) { item in
                        let components = calendar.dateComponents([.day, .month, .year], from: item)
                        let day = components.day!
                        let month = components.month!
                        let year = components.year!

                        Text("\(String(day)).\(String(month)).\(String(year))")
                            .tag(String(day))
                    }
                }

I did something like this, unfortunately I get an error: Cannot convert value of type 'Set<Date>' to expected argument type 'Binding<C>'

CodePudding user response:

Starting from @JoakimDanielson answer, code that preserves the original order and has ≈O(1) performance would look something like this (It could be made shorter. I left it verbose for ease of reading.)

var uniqueDates: [Date] {
    var result = [Date]()
    var uniqueDatesSet : Set<Date> = [] // Use a set to test for uniqueness
    let midnights = dates.map { Calendar.current.startOfDay(for: $0) }
    for date in midnights {
        if !uniqueDatesSet.contains(date) {
            uniqueDatesSet.insert(date)
            result.append(date)
        }
    }
    return result
}

Here is some working test code using the above, as a command line tool. (I wrote the test code to generate dates in random order so you can tell that the uniqueDates code preserves the original order of the array.)

Note that calculating unique dates would take a non-trivial amount of time for a large array of dates, so using a computed property isn't the best way to do this. It would be better to compute the unique dates the first time you ask for it after the source array of dates changes.

import Foundation

var dates: [Date] = []
let secondsInDay = Double(24 * 60 * 60)

for _ in 1...20 {
let now = Date()
    let random = Double.random(in: (-secondsInDay * 5.0)...secondsInDay*5)
    dates.append(now.addingTimeInterval(random))
}

var uniqueDates: [Date] {
    var result = [Date]()
    var uniqueDatesSet : Set<Date> = [] // Use a set to test for uniqueness
    let midnights = dates.map { Calendar.current.startOfDay(for: $0) }
    for date in midnights {
        if !uniqueDatesSet.contains(date) {
            uniqueDatesSet.insert(date)
            result.append(date)
        }
    }
    return result
}

print("Dates:")
dates.forEach { print(DateFormatter.localizedString(from:$0, dateStyle: .short, timeStyle: .none)) }
print("Unique dates:")
uniqueDates.forEach { print(DateFormatter.localizedString(from:$0, dateStyle: .short, timeStyle: .none)) }

A test run of that outputs:

Dates:
1/12/23
1/11/23
1/9/23
1/11/23
1/4/23
1/6/23
1/7/23
1/9/23
1/12/23
1/7/23
1/3/23
1/10/23
1/8/23
1/12/23
1/6/23
1/8/23
1/6/23
1/11/23
1/4/23
1/3/23
Unique dates:
1/12/23
1/11/23
1/9/23
1/4/23
1/6/23
1/7/23
1/3/23
1/10/23
1/8/23

CodePudding user response:

I would map to start of day and use Set to get unique values

Set(dates.map { calendar.startOfDay(for: $0)})

This could be done as a computed property in temperatureCall and then you can use this property in your ForEach instead

var uniqueDates: [Date] {
    Set(dates.map { calendar.startOfDay(for: $0) }).sorted()
}
  • Related