【问题标题】:Swift - need to sort an array of string days of weekSwift - 需要对一周中的字符串数组进行排序
【发布时间】:2018-11-03 20:52:43
【问题描述】:

斯威夫特 4

我有一个数组,其中包含随机的星期几文本。例如

var daysOfWeek: [String] = [] // ["Tuesday", "Thursday" , "Sunday", "Friday"]

我希望能够将它们排序为:周日、周一、周二等...

我不确定这是否是正确的方法,但我试过了..

     let dateFormatter = DateFormatter()

    for element in daysOfWeek {

        print(dateFormatter.weekdaySymbols[element])
    }

这会引发错误:

value of optional type '[String]?' must be unwrapped to refer to member 'subscript' of wrapped base type

我对 Xcode 和 Swift 还很陌生

这是正确的做法吗?如果是这样,我该如何解决这个错误?

如果这不是正确的做法,那是什么? 感谢您的帮助

【问题讨论】:

    标签: arrays swift sorting


    【解决方案1】:

    你可以像这样创建一个字典,将每个字符串对应一个数值:

    let weekDayNumbers = [
        "Sunday": 0,
        "Monday": 1,
        "Tuesday": 2,
        "Wednesday": 3,
        "Thursday": 4,
        "Friday": 5,
        "Saturday": 6,
    ]
    

    然后你就可以这样排序了:

    weekdays.sort(by: { (weekDayNumbers[$0] ?? 7) < (weekDayNumbers[$1] ?? 7) })
    

    这将在末尾排序非工作日字符串。

    另请注意,世界不同地区的一周开始时间不同。他们可能会以不同的方式订购东西。

    【讨论】:

    • 聪明的解决方案。 (已投票)我正在考虑使用日期格式化程序拼出的工作日符号将星期名称转换为整数,但你的更简单。 (另一方面,如果您希望解决方案适用于不同的本地语言,那么 DateFormatter 方法将是更好的选择。)
    • @DuncanC 提交您的解决方案可能也是值得的,因为您提出了一个关于语言环境的好观点。对一个问题有多种不同的解决方案总是好的,因为我们总是可以从答案中挑选出在其他情况下有用的元素。
    【解决方案2】:

    方法如下:

    let week = DateFormatter().weekdaySymbols!
    print(week)  //["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
    

    这样,日期名称和一周的第一天将根据当前的区域设置和系统设置自动设置。例如:

    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "fr-FR")
    let week = formatter.weekdaySymbols!
    print(week)  //["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
    

    对一组日期名称进行排序:

    let week = DateFormatter().weekdaySymbols!
    var daysOfWeek: [String] = ["Tuesday", "Thursday" , "Sunday", "Friday"]
    daysOfWeek.sort { week.firstIndex(of: $0)! < week.firstIndex(of: $1)!}
    print(daysOfWeek) //["Sunday", "Tuesday", "Thursday", "Friday"]
    

    为了简洁起见,我在这里强制展开。您可以使用以下方法检查 daysOfWeek 中的所有字符串是否有效:

    var daysOfWeek: [String] = ["Tuesday", "Thursday" , "Sunday", "Friday"]
    let week = DateFormatter().weekdaySymbols!
    guard Set(daysOfWeek).isSubset(of: week) else {
        fatalError("The elements of the array must all be day names with the first letter capitalized")
    }
    

    Mr Duncan 所建议的那样,为了使上述解决方案更快,这里有一种替代方法:

    let week = DateFormatter().weekdaySymbols!
    var dayDictionary: [String: Int] = [:]
    for i in 0...6 {
        dayDictionary[week[i]] = i
    }
    var daysOfWeek: [String] = ["Tuesday", "Thursday" , "Sunday", "Friday"]
    daysOfWeek.sort { (dayDictionary[$0] ?? 7) < (dayDictionary[$1] ?? 7)}
    print(daysOfWeek) //["Sunday", "Tuesday", "Thursday", "Friday"]
    

    使用字符串作为日期名称标识符容易出错。更安全的方法是使用枚举:

    enum WeekDay: String {
        case first      = "Sunday"
        case second     = "Monday"
        case third      = "Tuesday"
        case fourth     = "Wednesday"
        case fifth      = "Thursday"
        case sixth      = "Friday"
        case seventh    = "Saturday"
    }
    
    let week: [WeekDay] = [.first, .second, .third, .fourth, .fifth, .sixth, .seventh]
    var dayDictionary: [WeekDay : Int] = [:]
    for i in 0...6 {
        dayDictionary[week[i]] = i
    }
    var daysOfWeek: [WeekDay] = [.third, .fifth , .first, .sixth]
    daysOfWeek.sort { (dayDictionary[$0] ?? 7) < (dayDictionary[$1] ?? 7)}
    print(daysOfWeek.map {$0.rawValue}) //["Sunday", "Tuesday", "Thursday", "Friday"]
    

    【讨论】:

    • 如果您不需要日期格式化程序,只需使用Calendar.current.weekdaySymbols
    • 我正在尝试对那些日子的数组进行排序。
    • Carpsen,您的解决方案是在排序正文中使用Array.firstIndex(of:)。该方法很慢,并且具有O(n) 时间性能,因此如果您要对大量周名称进行排序,它会很慢。
    • 我尝试了你原来的基于数组的方法和我的基于字典的方法的基准,我的速度大约快了 5 倍。 但是,当我在编译器中打开速度优化时,基于数组的版本几乎快了 3 倍!谁会想到?字典使用散列进行查找,数组使用线性搜索。我不知道基于数组的版本如何更快!
    • @thibautnoah 这取决于语言环境
    【解决方案3】:

    我的解决方案是@sweeper 解决方案的变体:

    var inputDaysOfWeek: [String] = ["Tuesday", "Thursday" , "Sunday", "Friday", "Foo", "Bar"]
    print("inputDaysOfWeek = \(inputDaysOfWeek)")
    
    //Build a dictionary of days of the week using the current calendar,
    //which will use the user's current language
    //This step only needs to be done once, at startup.
    var weekdaysDict = [String: Int]()
    let weekdays = Calendar.current.weekdaySymbols.enumerated()
    weekdays.forEach { weekdaysDict[$0.1]  = $0.0 }
    //-----------------
    
    //If a weekday name doesn't match the array of names, use a value of -1, 
    //which will cause it to sort at the beginning of the sorted array.
    inputDaysOfWeek.sort {weekdaysDict[$0] ?? -1 < weekdaysDict[$1] ?? -1 }
    print("sorted inputDaysOfWeek = \(inputDaysOfWeek)")
    

    我的代码构建了一个包含工作日名称及其索引值的字典,就像 Sweeper 的回答一样。 Carpsen 使用firstIndex(of:) 的方法会起作用,但会更慢,而且对于大型字符串数组可能会慢很多。 我正在使用当前日历中的工作日符号,它将采用用户的区域设置/语言。如果您想将工作日名称数组强制为特定语言/区域设置,则可以改用为该语言/区域设置创建的 DateFormatter,如 Carpsen 的回答中所示。

    请注意,如果输入的工作日名称字符串的大小写值是不可预测的,那么您可能需要稍微更改上面的代码以将工作日名称字典小写,并将要排序的字符串数组小写,以便不同大小写的工作日名称仍然匹配。


    编辑:

    我编写了一个测试命令行工具,它同时使用基于数组的项目匹配(根据@Carpsen90 的第一个解决方案)和 my/Sweeper 的基于字典的匹配,发现基于数组的版本需要大约 5 倍的时间工作日名称的 1,000,000 元素数组。说实话,100 万件商品的 5 倍还不错。这表明这两种方法具有相同的时间复杂度。

    但是,当我在编译器中打开“优化速度”时,基于字典的方法会花费大约 2.8 倍的时间!

    下面是全部测试代码:

    //Build a dictionary of days of the week using the current calendar,
    //which will use the user's current language
    var weekdaysDict = [String: Int]()
    let weekdays = Calendar.current.weekdaySymbols
    let weekdayTuples = weekdays.enumerated()
    weekdayTuples.forEach { weekdaysDict[$0.1]  = $0.0 }
    //--------------- --
    
    func sortWeekDaysUsingDict(array: [String]) -> [String] {
        let result = array.sorted { weekdaysDict[$0] ?? -1 < weekdaysDict[$1] ?? -1 }
        return result
    }
    
    func sortWeekDaysUsingArray(array: [String]) ->  [String] {
        let result = array.sorted { weekdays.firstIndex(of: $0)! < weekdays.firstIndex(of: $1)! }
        return result
    }
    
    /*This function times a sorting function
     It takes an array to sort, a function name (for logging) and a function pointer to the sort function.
     It calculates the amount of time the sort function takes, logs it, and returns it as the function result.
     */
    func sortArray(array: [String],
                   functionName: String,
                   function: ([String]) -> [String]) -> TimeInterval {
        let start = Date().timeIntervalSinceReferenceDate
        let _ = function(array)
        let elapsed = Date().timeIntervalSinceReferenceDate - start
        print("\(functionName) for \(array.count) items took " + String(format: "%.3f", elapsed) + " seconds")
        return elapsed
    }
    
    //Build a large array of random day-of-week strings:
    var randomWeekdayNames = [String]()
    for _ in 1 ... 1_000_000 {
        randomWeekdayNames.append(weekdays.randomElement()!)
    }
    
    let time1 = sortArray(array: randomWeekdayNames,
                          functionName: "sortWeekDaysUsingDict(array:)",
                          function: sortWeekDaysUsingDict(array:))
    let time2 = sortArray(array: randomWeekdayNames,
                          functionName: "sortWeekDaysUsingArray(array:)",
                          function:  sortWeekDaysUsingArray(array:))
    
    if time1 > time2 {
        print("dict-based sorting took " + String(format:"%0.2f", time1/time2) + "x longer")
    } else {
        print("array-based sorting took " + String(format:"%0.2f", time2/time1) + "x longer")
    }
    

    关闭优化(调试默认)结果是:

    sortWeekDaysUsingDict(array:) for 1000000 items took 9.976 seconds
    sortWeekDaysUsingArray(array:) for 1000000 items took 59.134 seconds
    array-based sorting took 5.93x longer
    

    但是选择了“优化速度”,结果就大不相同了:

    sortWeekDaysUsingDict(array:) for 1000000 items took 3.314 seconds
    sortWeekDaysUsingArray(array:) for 1000000 items took 1.160 seconds
    dict-based sorting took 2.86x longer
    

    这很令人惊讶,我不知道如何解释。

    编辑#2:

    好的,我想通了。事实上,我们匹配的键的数组/字典中只有 7 个可能的值会扭曲结果。

    我做了另一个测试,而不是一周中的几天,我将拼写的数字从“一”排序到“一千”。在这种情况下,基于字典的方法要快很多,正如我预期的那样:

    针对时间性能进行了优化,并使用了 1,000 个唯一词:

    sortWeekDaysUsingDict(array:) for 100000 items took 0.520 seconds
    sortWeekDaysUsingArray(array:) for 100000 items took 85.162 seconds
    array-based sorting took 163.64x longer
    

    (处理 1000 个唯一词,基于数组的方法对 1,000,000 个随机词进行排序太慢了。我不得不将随机词的数量降低到 100,000 进行第二组测试。)

    【讨论】:

    • 枚举是避免日期名称中出现小写和随机字符串的更好方法
    • 解释你的枚举想法?
    • 类似enum WeekDay { case sunday; case monday; case tuesday; case wednesday; case thursday; case friday; case saturday }; let week: [WeekDay] = [.sunday, .monday, .tuesday, .wednesday, .thursday, .friday, .saturday]
    【解决方案4】:

    这是一个不需要创建包含工作日及其索引的字典的解决方案。

    func sortWeekDays(_ weekDays: [String]) -> [String]? {
        guard let correctOrder = DateFormatter().weekdaySymbols else {
            return nil
        }
        let result = weekDays.sorted {
            guard let firstItemIndex = correctOrder.firstIndex(of: $0),
                let secondItemIndex = correctOrder.firstIndex(of: $1) else {
                    return false
            }
            return firstItemIndex < secondItemIndex
        }
        return result
    }
    

    【讨论】:

      【解决方案5】:

      首先将数组转换为字符串并检查
      选定的日子 = ["星期五","星期四","星期一"]
      让 stringRepresentation = selectedDays.joined(separator: ",")

          var sequenceDays = [String]()
          if stringRepresentation.contains("Monday") {
              sequenceDays.append("Monday")
          }
          if stringRepresentation.contains("Tuesday") {
              sequenceDays.append("Tuesday")
          }
          if stringRepresentation.contains("Wednesday") {
              sequenceDays.append("Wednesday")
          }
          if stringRepresentation.contains("Thursday") {
              sequenceDays.append("Thrusday")
          }
          if stringRepresentation.contains("Friday") {
              sequenceDays.append("Friday")
          }
          if stringRepresentation.contains("Saturday") {
              sequenceDays.append("Saturday")
          }
          if stringRepresentation.contains("Sunday") {
              sequenceDays.append("Sunday")
          }
      
          print(sequenceDays)//["Monday","Thursday","Friday"]
      

      // 让Days = sequenceDays.joined(分隔符:“,”) 打印(天) 让 parsedDays = Days.replacingOccurrences(of: "day", with: "") 打印(解析天数)

      【讨论】:

      • 嗨 Hafeez,欢迎来到 Stack Overflow。你能更具体地解决这个用户的问题吗?您的回答构建了一个新数组,我认为这与他们对数组排序的问题不同。
      猜你喜欢
      • 2017-11-14
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      • 2017-10-07
      • 2016-07-18
      相关资源
      最近更新 更多