Как в Swift 5 эффективно извлекать и использовать элементы в многомерных массивах? - PullRequest
2 голосов
/ 29 апреля 2020

Впервые в программировании это мой 5-й день с использованием swift / xcode, будьте добры.

Я создал массив формата - [[A], [B], [C], [D], [E], [F]], ..., чтобы служить до стола. Требуется получить A - AM / PM и B, C - Время начала и проверить, находится ли текущее время (часы, минуты) в пределах D, E - Время окончания , если это так, напечатайте сообщение F на UILabel.

, например, если сейчас 3.04, моя программа будет использовать следующую таблицу для печати " go спать, я сообщу, когда его 4 "

// [[A], [B], [C], [D], [E], [F]], ...


let messageArray : Array = [
                [["AM"], [00], [00], [00], [01], ["it's \nmidnight"]],
                [["AM"], [00], [02], [02], [59], ["it's very late (or early), \nto be up"]],
                [["AM"], [03], [00], [03], [01], ["are you ready \nfor the 03 am call?"]],
                [["AM"], [03], [02], [03], [59], ["go to sleep \ni'll let you know when it's \n4"]],...

Я знаю, как получить часы и минуты от текущего времени.

let date = Date()
var calendar = Calendar.current

let hour = calendar.component(.hour, from: date)
let minute = calendar.component(.minute, from: date)

и что мне нужно внешний и внутренний l oop для прохождения каждого x, y матрицы. Для каждой ячейки мне нужно извлечь массив и взять первые 4 элемента - AM / PM, startHour, startMinute, endHour, endMinute, чтобы проверить, находятся ли текущий час, минута в этих границах; но я не уверен, что понимаю синтаксический анализ многомерных массивов. Я пробовал разные вещи и пытался искать онлайн, но изо всех сил.

Должен ли я даже использовать для этого Массивы, есть ли какая-то другая, более эффективная структура данных?

Буду признателен за любую помощь или совет.

Спасибо,

Ответы [ 4 ]

1 голос
/ 29 апреля 2020

Вот крутой ответ с ClosedRange. Он позволяет вам создавать собственные диапазоны времени и может давать вам несколько сообщений в зависимости от того, какое время вы выбрали.

struct Time { var hour: Int, min: Int }
extension Time: Comparable, Hashable {
    static func < (lhs: Time, rhs: Time) -> Bool {
        return lhs.hour < rhs.hour || (lhs.hour == rhs.hour && lhs.min < rhs.min)
    }
    init(_ h: Int,_ m: Int) { hour = h; min = m }
}

extension Dictionary where Key == ClosedRange<Time> {
    func has(_ time: Time) -> [Value] {
        var messages = [Value]()
        for i in self {
            time >= i.key.lowerBound && time <= i.key.upperBound ? { messages.append(i.value) }() : {}()
        }
        return messages
    }
}

let a = [
    Time(0, 0)...Time(0, 1): "it's midnight",
    Time(0, 2)...Time(2, 59): "it's very early",
    Time(3, 0)...Time(3, 1): "that 3am call?",
    Time(3, 2)...Time(3,59): "come back at 4",
    Time(3, 50)...Time(3, 59): "almost 4"
]

a.has(Time(3, 58))
// returns ["come back at 4", "almost 4"]
1 голос
/ 29 апреля 2020

Это немного сложно, потому что Date моделирует и дату, и время, и нет аналога, который моделирует только время или просто дату. Я импровизирую, используя кортеж целых чисел. Возможно, это не лучшее решение, но это лучшее, что я мог бы придумать (предложения приветствуются!)

Я бы использовал структуру для моделирования каждого сообщения (с startTime, endTime и вычисленным свойство, которое позволяет вам получить DateInterval для него.

Я бы также сделал TimedMessageList, в котором хранится справочная таблица, связывающая DateInterval с Message с. Поскольку поиск включает в себя проверку интервалов и не точные совпадения, Dictionary нам не очень поможет, поэтому я просто использую массив кортежей, линейный поиск по ним для каждого поиска.

Если производительность этого становится проблемой, вы может исследовать древовидные структуры данных, такие как Interval Tree . Хотя это добавляет много сложности и, вероятно, будет на самом деле медленнее, чем линейный поиск, пока вы не начнете искать тысячи записей или больше.

Структура будет более подходящей здесь:

import Foundation

struct Message {
    let startTime: (h: Int, m: Int)
    let endTime: (h: Int, m: Int)
    let message: String

    var applicableTimeIntererval: DateInterval { 
        let calendar = Calendar.current
        let now = Date()

        return DateInterval(
            start: calendar.date(bySettingHour: startTime.h, minute: startTime.m, second: 0, of: now)!, // TODO: are these safe to force unwrap?
            end:   calendar.date(bySettingHour:   endTime.h, minute:   endTime.m, second: 0, of: now)!  // TODO: are these safe to force unwrap?
        )
    }
}

struct TimedMessageList {
    let messagesByTimeInterval: [(dateInterval: DateInterval, message: Message)]

    init(_ messages: [Message]) {
        self.messagesByTimeInterval = messages.map { msg in (msg.applicableTimeIntererval, msg) }
    }

    func lookUpMessage(forTime timeTuple: (h: Int, m: Int)) -> Message? {
        let time = Calendar.current.date(bySettingHour: timeTuple.h, minute: timeTuple.m, second: 0, of: Date())! // TODO: are these safe to force unwrap?
        return self.messagesByTimeInterval.first(where: { (timeInterval: DateInterval, msg: Message) -> Bool in
            timeInterval.contains(time)
        })?.message
    }
}

let messages = TimedMessageList([
    Message(startTime: (h: 00, m: 00), endTime: (h: 00, m: 01), message: """
    it's 
    midnight
    """),
    Message(startTime: (h: 00, m: 02), endTime: (h: 02, m: 59), message: """
    it's very late (or early), 
    to be up
    """),
    Message(startTime: (h: 03, m: 00), endTime: (h: 03, m: 01), message: """
    are you ready 
    for the 03 am call?
    """),
    Message(startTime: (h: 03, m: 02), endTime: (h: 03, m: 59), message: """
    go to sleep  
    i'll let you know when it's 
    4
    """),
])

let messageFor0305 = messages.lookUpMessage(forTime: (h: 03, m: 05))
if let messageFor0305 = messageFor0305 {
    print(messageFor0305.message)
} else {
    print("There is no message for 3:05")
}
1 голос
/ 29 апреля 2020

Вы можете создать свою собственную структуру, что-то вроде этого:

enum AmPm {
    case am, pm
}
struct TimeMessage {
    var ampm: AmPm
    var hourStart: Int
    var minuteStart: Int
    var hourEnd: Int
    var minuteEnd: Int
    var message: String
}

С некоторыми удобными методами и свойствами:

extension TimeMessage {
    init(_ ampm: AmPm, _ hourStart: Int, _ minuteStart: Int, _ hourEnd: Int, _  minuteEnd: Int, _ message: String) {
        self.ampm = ampm
        self.hourStart = hourStart
        self.minuteStart = minuteStart
        self.hourEnd = hourEnd
        self.minuteEnd = minuteEnd
        self.message = message
    }
}

extension TimeMessage {
    var hourStart24: Int {
        switch ampm {
        case .am:
            return hourStart
        case .pm:
            return hourStart + 12
        }
    }

    var hourEnd24: Int {
        switch ampm {
        case .am:
            return hourEnd
        case .pm:
            return hourEnd + 12
        }
    }
}

extension TimeMessage {
    func matches(_ date: Date) -> Bool {
        let calendar = Calendar.current

        let hour = calendar.component(.hour, from: date)
        let minute = calendar.component(.minute, from: date)

        return (hourStart24, minuteStart) <= (hour, minute)
            && (hour, minute) <= (hourEnd24, minuteEnd)
    }
}

Используя вышеупомянутую структуру, вы можете объявить свой messageArray as:

let messageArray: [TimeMessage] = [
    TimeMessage(.am, 00, 00, 00, 01, "it's \nmidnight"),
    TimeMessage(.am, 00, 02, 02, 59, "it's very late (or early), \nto be up"),
    TimeMessage(.am, 03, 00, 03, 01, "are you ready \nfor the 03 am call?"),
    TimeMessage(.am, 03, 02, 03, 59, "go to sleep \ni'll let you know when it's \n4"),
    //...
]

И использовать его как:

let now = Date()
if let timeMessage = messageArray.first(where: {$0.matches(now)}) {
    print(timeMessage.message)
} else {
    print("No match")
}
1 голос
/ 29 апреля 2020

Используйте словарь типа [(Час, Минута): Строка]

let hour = Calendar.current.component(.hour, from: Date())
let minute = Calendar.current.component(.minute, from: Date())

let messageArray: [(Int, Int):String] = [
 (0, 1): ["it's\nmidnight"],
 (2, 59): ["it's\very early"],
 (3, 01): ["that 3am call?"],
 (3, 59): ["come back at 4"]
]

Затем вы можете использовать функциональное программирование для проверки порядка.

extension Dictionary where Key == (Int, Int) {
    func smallestValue<T: Value>(_ time: (Int, Int)) -> T? {
        let message: T? = nil
        for i in self {
            // Check for smallest case that is good
        }
        return message
    }
}

let message = messageArray.smallestValue((hour, minute)) ?? "Good job for being awake"
print(message)

Возможно, потребуется сделать структура для (Int, Int)

...