【问题标题】:Group and sort an array in Swift在 Swift 中对数组进行分组和排序
【发布时间】:2015-07-10 16:34:19
【问题描述】:

假设我有这个代码:

class Stat {
   var statEvents : [StatEvents] = []
}

struct StatEvents {
   var name: String
   var date: String
   var hours: Int
}

var currentStat = Stat()

currentStat.statEvents = [
   StatEvents(name: "lunch", date: "01-01-2015", hours: 1),
   StatEvents(name: "dinner", date: "02-01-2015", hours: 2),
   StatEvents(name: "dinner", date: "03-01-2015", hours: 3),
   StatEvents(name: "lunch", date: "04-01-2015", hours: 4),
   StatEvents(name: "dinner", date: "05-01-2015", hours: 5),
   StatEvents(name: "breakfast", date: "06-01-2015", hours: 6),
   StatEvents(name: "lunch", date: "07-01-2015", hours: 7),
   StatEvents(name: "breakfast", date: "08-01-2015", hours: 8)
]

我想知道是否有办法获得一个输出如下的数组:

- [0]
  - name : "lunch"
  - date
    - [0] : "01-01-2015"
    - [1] : "04-01-2015"
    - [2] : "07-01-2015"
  - hours
    - [0] : 1
    - [1] : 4
    - [2] : 7     
- [1]
  - name : "dinner"
  - date
    - [0] : "02-01-2015"
    - [1] : "03-01-2015"
    - [2] : "05-01-2015"
  - hours
    - [0] : 2
    - [1] : 3   
    - [2] : 5          
- [2]
  - name : "breakfast"
  - date
    - [0] : "06-01-2015"
    - [1] : "08-01-2015"
  - hours
    - [0] : 6
    - [1] : 8 

如您所见,最终数组应按“名称”后代分组。 @oisdk 你能看看这个吗??

【问题讨论】:

    标签: arrays swift sorting


    【解决方案1】:

    这可能看起来有点矫枉过正,但这是我想到的解决方案。

    extension Array {
        /**
        Indicates whether there are any elements in self that satisfy the predicate.
        If no predicate is supplied, indicates whether there are any elements in self.
        */
        func any(predicate: T -> Bool = { t in true }) -> Bool {
            for element in self {
                if predicate(element) {
                    return true
                }
            }
            return false
        }
    
        /**
        Takes an equality comparer and returns a new array containing all the distinct elements.
        */
        func distinct(comparer: (T, T) -> Bool) -> [T] {
            var result = [T]()
            for t in self {
                // if there are no elements in the result set equal to this element, add it
                if !result.any(predicate: { comparer($0, t) }) {
                    result.append(t)
                }
            }
            return result
        }
    }
    
    let result = currentStat.statEvents
        .map({ $0.name })
        .distinct(==)
        .sorted(>)
        .map({ name in currentStat.statEvents.filter({ $0.name == name }) })
    

    现在你有一个列表列表,其中第一个列表包含晚餐类型的所有 statEvents,下一个列表包含午餐类型的事件,等等。

    明显的缺点是这可能不如其他解决方案的性能。好的部分是您不必依赖并行数组来获取与特定日期关联的小时数。

    【讨论】:

      【解决方案2】:

      我的看法:

      extension StatEvents : Comparable {}
      
      func < (lhs:StatEvents, rhs:StatEvents) -> Bool {
          if lhs.name != rhs.name {
              return lhs.name > rhs.name
          } else if lhs.date != rhs.date {
              return lhs.date < rhs.date
          } else {
              return lhs.hours < rhs.hours
          }
      }
      
      func == (lhs:StatEvents, rhs:StatEvents) -> Bool {
          return lhs.name == rhs.name
              && lhs.date == rhs.date
              && lhs.hours == rhs.hours
      }
      
      struct ResultRow {
          var name: String
          var dates: [String]
          var hours: [Int]
      }
      
      var result : [ResultRow] = []
      
      let sorted = currentStat.statEvents.sort()
      for event in sorted {
          if result.last?.name != event.name {
              result.append(ResultRow(name: event.name, dates: [], hours: []))
          }
          result[result.endIndex - 1].dates.append(event.date)
          result[result.endIndex - 1].hours.append(event.hours)
      }
      

      测试:

      for r in result { print(r) }
      

      打印:

      p.ResultRow(name: "lunch", dates: ["01-01-2015", "04-01-2015", "07-01-2015"], hours: [1, 4, 7])
      p.ResultRow(name: "dinner", dates: ["02-01-2015", "03-01-2015", "05-01-2015"], hours: [2, 3, 5])
      p.ResultRow(name: "breakfast", dates: ["06-01-2015", "08-01-2015"], hours: [6, 8])
      

      【讨论】:

        【解决方案3】:

        已经有一些答案了,但到底是什么,这很有趣。我的回答并没有使用 Swift 中的很多高阶函数,但它仍然可以完成工作:

        // Get the list of unique event names
        var eventNames = [String]()
        for event in currentStat.statEvents {
            if !eventNames.contains(event.name) {
                eventNames.append(event.name)
            }
        }
        
        // The type of the result
        struct ResultType {
            var name : String
            var date : [String]
            var hours : [Int]
        }
        
        var result = [ResultType]()
        for name in eventNames {
            let matchingEvents = currentStat.statEvents.filter { $0.name == name }
            let dates = matchingEvents.map { $0.date }
            let hours = matchingEvents.map { $0.hours }
        
            result.append(ResultType(name: name, date: dates, hours: hours))
        }
        

        【讨论】:

        • 感谢您的帮助!
        【解决方案4】:

        最终结果要么是 [[String : AnyObject]] 类型,要么您创建一个保存这些值的新结构类型,结果是 [String : NewStructType] 类型:

        struct NewStructType
        { 
            var dates: [String]?
            var hours: [Int]?
        }
        

        因此,您必须对此做出决定,然后您必须编写自己的函数来对您的 StatEvents 对象进行排序和分组。也许你可以优化它的性能,但这里是如何实现第二个版本(使用 NewStructType)的第一个想法:

        var result = [String : NewStructType]()
        
        for statEvent in currentStat.statEvents
        {
            if (result[statEvent.name] != nil)
            {
                var newStructType = result[statEvent.name]!
        
                newStructType.dates.append(statEvent.date)
                newStructType.hours.append(statEvent.hours)
            }
            else
            {
                result[statEvent.name] = NewStructType(dates: [statEvent.date], hours: [statEvent.hours])
            }
        }
        

        【讨论】:

        • 无需在结构定义中将日期和时间设为可选。它也可以有一个初始化器,它需要一个日期 [String] 和一个小时 [Int]。此外,如果这是一次性使用,则类型可以简单地为 '[String : (dates: [String], hours: [Int])]'。但是,如果您将此值传递给本地上下文或类之外的调用者,那么具有结构会很好。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-08-13
        • 2014-06-20
        • 1970-01-01
        • 2018-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多