如何在Swift中从一组元组创建一个条纹

时间:2016-07-22 15:59:39

标签: arrays swift sorting tuples

我有一组看起来像这样的元组:

[("07-20-2016", 2), ("07-22-2016", 5.0), ("07-21-2016", 3.3333333333333335)]

它包含一个日期的字符串和一个情绪的双重字母(心情小于或等于3表示那天是美好的一天)。 我想弄清楚如何创造美好的一天。

所以我认为我首先需要做的是对元组数组进行排序,按日期排序。 然后我需要计算低于心情值3的元组,如果它们在一行中。在这种情况下,结果将是1,因为只有1天低于3。

如果我有一系列元组,如:

[("07-22-2016", 5.0), ("07-21-2016", 3), ("07-20-2016", 2), ("07-19-2016", 1), ("07-18-2016", 1), ("07-16-2016", 2), ("07-15-2016", 3)]

它的结果是4,因为连续的最大天数低于3是4.它不是6的原因是因为" 07-17-2016& #34;不存在。

请帮我解决这个问题。我知道这里有很多用户比我聪明。你能解决这个问题吗?

3 个答案:

答案 0 :(得分:2)

这是我找到的答案。

func datesToStreaks(input: [NSDate]) -> [[NSDate]] {
        var ranges:[[NSDate]] = [[]]
        input.forEach { currentDay in
            var lastRange = ranges.last ?? []
            if let lastDate = lastRange.last {
                if lastDate.dateByAddingTimeInterval(86400).isEqualToDate(currentDay) {
                    lastRange.append(currentDay)
                    ranges.removeLast()
                    ranges.append(lastRange)
                }
                else {
                    ranges.append([currentDay])
                }
            }
            else {
                lastRange.append(currentDay)
                ranges.removeLast()
                ranges.append(lastRange)
            }

        }
        return ranges
    }


    let input = [("07-22-2016", 5.0), ("07-21-2016", 3), ("07-20-2016", 2), ("07-19-2016", 1), ("07-18-2016", 1), ("07-16-2016", 2), ("07-15-2016", 3)]
    let sortedDays:[NSDate] = input
        .filter { (date, mood) in
            mood <= 3.0}
        .map { (date, mood) in
            return date}
        .flatMap{ date in
            let dateFormatter = NSDateFormatter()
            dateFormatter.dateFormat = "MM/dd/yyyy"
            return dateFormatter.dateFromString(date)
        }
        .sort{$0.0.earlierDate($0.1).isEqualToDate($0.0)}

    let maxStreak = datesToStreaks(sortedDays)
        .map{$0.count}
        .reduce(0, combine: {return max($0,$1)})

    print(maxStreak) // 4

答案 1 :(得分:2)

首先,正如matt建议的那样,制作更好的数据类型。这种语言使结构非常方便。我们需要每个条目都有一个日期和心情分数。易于peasy。

import Foundation

struct JournalEntry {
    let date: NSDate
    let score: Double

    var isGoodDay: Bool { return self.score <= 3.0 }
}

(计算出的isGoodDay属性将使以后的生活更加轻松。)

现在我们需要将元组的原始列表转换为这些结构的列表。这很简单。创建一个日期解析器,并使用map在元组类型和结构之间进行转换:

typealias RawEntry = (String, Double)
func parseRawEntries<S: SequenceType where S.Generator.Element == RawEntry>
                    (rawEntries: S)
    -> [JournalEntry]
{
    let parser = NSDateFormatter()
    parser.dateFormat = "MM-dd-yyyy"
    // Use guard and flatMap because dateFromString returns an Optional; 
    // you might prefer to throw an error to indicate that the data are 
    // incorrectly formatted
    return rawEntries.flatMap { rawEntry in
        guard let date = parser.dateFromString(rawEntry.0) else {
            return nil
        }
        return JournalEntry(date: date, score: rawEntry.1)
    }
}

转换后我们需要通过列表测试两个条件:首先,那些日子是“好”然后那对好日子是连续的。我们在数据结构本身中有这种辅助方法来测试前者。让我们为连续部分创建另一个辅助函数:

/* N.B. for brevity this assumes first and second are already sorted! */
func entriesConsecutive(first: JournalEntry, _ second: JournalEntry) -> Bool {
    let calendar = NSCalendar.currentCalendar()
    let dayDiff = calendar.components(.Day,
                                      fromDate: first.date,
                                      toDate: second.date,
                                      options: NSCalendarOptions(rawValue: 0))
    return dayDiff.day == 1
}

在每个枚举步骤中,我们将获得当前条目,但我们还需要前一个或下一个,以便我们可以测试连续性。我们显然也希望跟踪条纹的数量,因为我们发现它们。让我们为它做另一种数据类型。 (lastEntry而不是nextEntry的原因很快就会明确。)

struct Streak {
    let lastEntry: JournalEntry
    let count: UInt
}

这是有趣的部分:迭代数组,构建Streak s。这可以通过for循环完成。但是,通过计算和聚集每个新项目来消耗序列是一种已知模式。它被称为“folding”; Swift使用备用术语reduce()

将序列缩减为组,或者在跟踪某些事物的同时,也是众所周知的模式:组合值本身就是一个集合。在每个步骤中,您将检查新项目以及还原中的最新值。减少的新值 - 从组合函数返回的内容 - 仍然是集合,可以更新先前的值或附加新值。

上面的Streak数据类型将使缩减中的元素:这就是它使用 last 条目的原因。每一步,我们都会保存JournalEntry并在下一步检查它。

对于条纹,我们只关心“好”的日子。让我们让生活变得简单,filter就可以了。let goodDays = journal.filter { $0.isGoodDay }我们需要第一个条目,这样才能在第一步中进行比较。如果由于某种原因 没有好日子,这也是一个拯救的机会。

guard let firstEntry = goodDays.first else {
    return []
}

第一个条目进入Streak对象,该对象将进入将成为缩减的集合:let initial = [Streak(lastEntry: firstEntry, count: 1)],我们已准备就绪。完整的函数看起来像这样(我将让注释解释reduce中的过程):

func findStreaks(in journal: [JournalEntry]) -> [Streak] {
    let goodDays = journal.filter { $0.isGoodDay }
    guard let firstEntry = goodDays.first else {
        return []
    }
    let initial = [Streak(lastEntry: firstEntry, count: 1)]
    return 
        // The first entry is already used; skip it.
        goodDays.dropFirst()
                .reduce(initial) { 
                  (reduction: [Streak], entry: JournalEntry) in
                    // Get most recent Streak for inspection
                    let streak = reduction.last!    // ! We know this exists

                    let consecutive = entriesConsecutive(streak.lastEntry, 
                                                         entry)
                    // If this is the same streak, increment the count by
                    // replacing the previous value.
                    // Otherwise add a new Streak with count 1.
                    let newCount = consecutive ? streak.count + 1 : 1
                    let streaks = consecutive ? 
                                  Array(reduction.dropLast()) :
                                  reduction
                    return streaks + 
                            [Streak(lastEntry: entry, count: newCount)]
                }
}

现在,回到顶层,事情很简单。原始数据:

let rawEntries = [("07-22-2016", 5.0), ("07-21-2016", 3), ("07-20-2016", 2), ("07-19-2016", 1), ("07-18-2016", 1), ("07-16-2016", 2), ("07-15-2016", 3)]

将其转换为相关的数据类型,在我们进入时立即按日期排序。

let journal = parseRawEntries(rawEntries).sort { $0.date < $1.date }

计算条纹:

let streaks = findStreaks(in: journal)

然后你想要的结果是let bestStreakLen = streaks.map({ $0.count }).maxElement()

(小记:使用NSDate比较<需要新功能:

func <(lhs: NSDate, rhs: NSDate) -> Bool {
    let earlier = lhs.earlierDate(rhs)
    return earlier == lhs
}

答案 2 :(得分:1)

你可以先建立好心情和坏心情的条纹数组,然后过滤或减少它来分析streeks:

Incorrect