0

使用 Swift 2.1 我正在尝试创建一个函数,该函数按键 dateKey 的日期值对字典数组进行排序。

我想将此作为 Array 类型的扩展添加,所以我可以使用它来调用它someArray.sortDictionariesByDate(dateKey: String, dateFormatter: NSDateFormatter)

extension Array where Element: CollectionType {
  mutating func sortDictionariesByDate(dateKey: String, dateFormatter: NSDateFormatter) {
    sortInPlace {
      dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
      if let dateStringA: String = ($0 as? [String: AnyObject])?[dateKey] as? String, let dateStringB = ($1 as? [String: AnyObject])?[dateKey] as? String {
        if let dateA: NSDate = dateFormatter.dateFromString(dateStringA), dateB: NSDate = dateFormatter.dateFromString(dateStringB) {
          return dateA.compare(dateB) == .OrderedAscending
        }
      }
      return false
    }
  }
}

只要将字典键入为 ,这就可以正常工作[String: AnyObject],但是如果我在类型的字典上使用它,[String: String]它就不起作用,因为它无法转换StringAnyObject. 我认为这是因为Stringis a Structnot a Class。我也尝试将元素类型转换为[String: Any],但无论使用类型为[String: AnyObject]或的字典,它都不起作用[String: String]

是否有任何转换可以用来支持具有键类型String和任何值类型(StringAnyObject)的字典,或者可以添加到扩展中以避免完全转换的 where 子句或协议一致性?

编辑:这是根据请求的两个示例数组

var sampleArray1: [[String: AnyObject]] = [["date": "2015-10-24T13:00:00.000Z", "foo": "bar"], ["date": "2015-10-24T14:00:00.000Z", "foo": "bar"]]
sampleArray1.sortDictionariesByDate("date", dateFormatter: NSDateFormatter())
var sampleArray2: [[String: String]] = [["date": "2015-10-24T13:00:00.000Z", "foo": "bar"], ["date": "2015-10-24T14:00:00.000Z", "foo": "bar"]]
sampleArray2.sortDictionariesByDate("date", dateFormatter: NSDateFormatter())
4

3 回答 3

2

第一个问题:您正在比较字符串,而不是日期。幸运的是,您的字符串的格式使其可以直接与字符串和它所代表的日期值进行比较。因此,您根本不需要将其转换为NSDate

第二个问题是对Dictionary<Key,Value1>批发的类型转换Dictionary<Key,Value2>不起作用,即使在. 它可能在像这样的简单例子中工作......Value1Value2

let a : [String: String] = ["name": "David", "location": "Chicago"]
let b = a as [String: AnyObject]

...因为编译器可以看到其中的值类型 ( String)a并通过编译时对其进行优化。对于像您这样的动态情况,没有提前可用的信息。


当你需要活力时,你总是可以回到老朋友Objective-C:

extension Array where Element: CollectionType {
    mutating func sortDictionariesByDate(dateKey: String) {
        self.sortInPlace {
            if let a = $0 as? NSDictionary, b = $1 as? NSDictionary {
                return (a[dateKey] as? String) < (b[dateKey] as? String)
            } else {
                return false
            }
        }
    }
}

// Example:
var sampleArray1: [[String: AnyObject]] = [
    ["date": "2015-10-24T13:00:00.000Z", "foo": "bar"],
    ["date": "2015-10-24T14:00:00.000Z", "foo": "bar"],
    ["date": "2015-10-24T10:00:00.000Z", "foo": "bar"]
]
var sampleArray2: [[String: String]] = [
    ["date": "2015-10-24T13:00:00.000Z", "foo": "bar"], 
    ["date": "2015-10-24T14:00:00.000Z", "foo": "bar"],
    ["date": "2015-10-24T10:00:00.000Z", "foo": "bar"]
]

sampleArray1.sortDictionariesByDate("date")
sampleArray2.sortDictionariesByDate("date")

请注意,由于您现在比较的是字符串而不是日期,NSDateFormatter因此不需要。

于 2015-10-22T20:42:33.520 回答
1

你要求做的是愚蠢的。您不会编写一个既可以同时使用[[String:AnyObject]]也可以[[String:String]]不使用强制转换的函数。Swift 有严格的类型。就是这样。铸造并快乐!

转换像字典这样复杂类型的数组的技巧是使用map单独转换每个元素(这实际上是数组转换在幕后所做的):

let sampleArray1: [[String: AnyObject]] = [["date": "2015-10-24T13:00:00.000Z", "foo": "bar"], ["date": "2015-10-24T14:00:00.000Z", "foo": "bar"]]
let sampleArray2: [[String: String]] = [["date": "2015-10-24T13:00:00.000Z", "foo": "bar"], ["date": "2015-10-24T14:00:00.000Z", "foo": "bar"]]

func sortByDate(arr:[[String:AnyObject]]) -> [[String:AnyObject]] {
    let result = arr.sort {
        d1, d2 in
        let dat1 = NSDateFormatter().dateFromString(d1["date"] as! String)!
        let dat2 = NSDateFormatter().dateFromString(d2["date"] as! String)!
        return dat1.timeIntervalSinceReferenceDate < dat2.timeIntervalSinceReferenceDate
    }
    return result
}

let arr1 = sortByDate(sampleArray1)
let arr2 = sortByDate(sampleArray2.map{$0 as [String:AnyObject]})
// and if you have to, `map` arr2 back down to [String:String]

我承认我没有完全填写规范,但是添加更多参数很容易,以便您提供密钥名称和格式化程序。请注意,该示例实际上会在您的数据上崩溃,因为我们没有匹配的日期格式。您需要提供有效的格式。

我还认为(作为同一点的一部分)将日期字符串放入字典而不是实际日期是很愚蠢的。但我只是为了论证而同意这个前提。

于 2015-10-22T20:59:58.120 回答
0

这是一个不同的解决方案,它在处理函数内的转换时将函数作为扩展,Array从而避免.map()在处理不同类型的字典时来回使用。缺点是您必须提前知道函数支持的类型,而 @matt 的解决方案允许您在调用方法时处理转换,但代价是不是扩展,不是就地排序和映射回向前。

extension Array where Element: CollectionType {
  mutating func sortDictionariesByDate(dateKey: String, dateFormatter: NSDateFormatter) {
    sortInPlace {
      dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
      var dateString1: String?
      var dateString2: String?

      if let a = $0 as? [String: AnyObject], let b = $1 as? [String: AnyObject] {
        if let dateStringA: String = a[dateKey] as? String, dateStringB: String = b[dateKey] as? String {
          dateString1 = dateStringA
          dateString2 = dateStringB
        }
      } else if let a = $0 as? [String: String], let b = $1 as? [String: String] {
        dateString1 = a[dateKey]
        dateString2 = b[dateKey]
      }

      if let dateString1: String = dateString1, let dateString2: String = dateString2 {
        if let date1: NSDate = dateFormatter.dateFromString(dateString1), date2: NSDate = dateFormatter.dateFromString(dateString2) {
          return date1.timeIntervalSinceReferenceDate < date2.timeIntervalSinceReferenceDate
        }
      }
      return false
    }
  }
}

// Example:
var sampleArray1: [[String: AnyObject]] = [
    ["date": "2015-10-24T13:00:00.000Z", "foo": "bar"],
    ["date": "2015-10-24T14:00:00.000Z", "foo": "bar"],
    ["date": "2015-10-24T10:00:00.000Z", "foo": "bar"]
]
var sampleArray2: [[String: String]] = [
    ["date": "2015-10-24T13:00:00.000Z", "foo": "bar"], 
    ["date": "2015-10-24T14:00:00.000Z", "foo": "bar"],
    ["date": "2015-10-24T10:00:00.000Z", "foo": "bar"]
]

sampleArray1.sortDictionariesByDate("date")
sampleArray2.sortDictionariesByDate("date")
于 2015-10-22T22:17:51.387 回答