简体   繁体   English

Swift - 在很长的时间范围内按一年中的一周对一系列项目进行分组

[英]Swift - Group an array of items by week of year over a large time frame

I'm currently working on a project that requires me to manipulate an array of objects that take a date and a double value.我目前正在从事一个项目,该项目要求我操作一组带有日期和双精度值的对象。 I am able to break it down in a dictionary of years and then those nested arrays down into weeks of that year.我能够将其分解为年份字典,然后将这些嵌套数组分解为当年的几周。 Unfortunately the first week of the year is often present in one of the years and the following.不幸的是,一年中的第一周经常出现在其中一年和接下来的一年中。

What I want to achieve is an array or dictionary of all dates grouped down to each individual week and the sum of all amounts in that group.我想要实现的是一个数组或字典,将所有日期分组到每个单独的星期以及该组中所有金额的总和。

I've already tried grouping them by .weekOfYear , how ever that doesn't take into consideration the year itself, and if the two dates were more than a year apart, I would always only have an array 52 entries long.我已经尝试按.weekOfYear对它们进行分组, .weekOfYear这并没有考虑到年份本身,如果两个日期相隔超过一年,我将始终只有一个长度为 52 个条目的数组。

I've also looked into using the ordinality of the week by using Calendar.current.ordinality(of: .weekOfYear, in: .era, for: 'date') but it gets converted to an integer and I'm not sure how to then return it back to a date我还研究过使用Calendar.current.ordinality(of: .weekOfYear, in: .era, for: 'date')来使用一周的序数,但它被转换为整数,我不确定如何然后将其返回到某个日期

Below is a copy of my code that I'm using in an xCode playground and it's output下面是我在 xCode 游乐场中使用的代码副本,它是输出

import Foundation

let cal = Calendar.current

extension Date {
    var year: Int {
        return cal.component(.year, from: self)
    }
    var week: Int {
        return cal.component(.weekOfYear, from: self)
    }
    var weekAndYear: DateComponents {
        return cal.dateComponents([.weekOfYear, .year], from: self)
    }
}

struct Item {
    var date: Date
    var amount: Double
}

let startDate: Date = cal.date(from: DateComponents(year: 2018, month: 12, day: 2))!
let endDate: Date = cal.date(from: DateComponents(year: 2019, month: 1, day: 28))!

// Here I'm creating dummy items for each date between our date range
func setUpItems (startDate: Date, endDate: Date) -> [Item] {
    var date = startDate
    var inputArray: [Item] = []

    let dateCount = cal.dateComponents([.day], from: startDate, to: endDate).day
    let valuePerDay: Double = 1000 / Double(dateCount!)
    let lowerLim = valuePerDay - (valuePerDay / 2)
    let upperLim = valuePerDay + (valuePerDay / 2)

    while date < endDate {
        let input = Double.random(in: lowerLim...upperLim).rounded()
        inputArray.append(Item(
            date: date,
            amount: input
        ))
        date = cal.date(byAdding: .day, value: 1, to: date)!
    }

    return inputArray
}

let itemArray = setUpItems(startDate: startDate, endDate: endDate)


let grouppedByYear = Dictionary(grouping: itemArray, by: { $0.date.year })

let grouppedByYearThenWeek = grouppedByYear
    .mapValues{(yearArray: [Item]) -> Dictionary<Int, Double> in
        return Dictionary(grouping: yearArray, by: { $0.date.week })
            .mapValues{ (value: [Item]) in
                return value.map{ $0.amount }.reduce(0, +)
        }
    }

print("Grouped by week")
print(grouppedByYearThenWeek)

From this you can expect the output to look something like this由此您可以期望输出看起来像这样

Grouped by week
[2018: [49: 127.0, 52: 125.0, 50: 119.0, 51: 113.0, 1: 39.0],
  2019: [3: 124.0, 4: 122.0, 5: 19.0, 1: 87.0, 2: 121.0]]

I would hope to end up with a dictionary that doesn't split the week up dependent on the year我希望最终能得到一本不会根据年份划分一周的字典

[2018: [49: 127.0, 52: 125.0, 50: 119.0, 51: 113.0],
  2019: [3: 124.0, 4: 122.0, 5: 19.0, 1: 126.0, 2: 121.0]]

// or potentially

[["49 2018": 127.0, "52 2018": 125.0...],
  ["3 2019": 124.0, "4 2019": 122.0...]]

Any help would be greatly appreciated.任何帮助将不胜感激。

Unfortunately the first week of the year is often present in one of the years and the following.不幸的是,一年中的第一周经常出现在其中一年和接下来的一年中。

Yes.是的。 But if you're retrieving the week of year, it makes no sense to retrieve that with .year .但是,如果您要检索一年中的第几周,则用.year检索它是没有意义的。 You want to use .yearForWeekOfYear in conjunction with .weekOfYear :您想将.yearForWeekOfYear.weekOfYear结合使用:

var weekAndYear: DateComponents {
    return cal.dateComponents([.weekOfYear, .yearForWeekOfYear], from: self)
}

var yearForWeekOfYear: Int {
    return cal.component(.yearForWeekOfYear, from: self)
}

And you might find it useful to have a “start of week” computed property:您可能会发现拥有“一周的开始”计算属性很有用:

var startOfWeek: Date {
    let components = cal.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)
    return cal.date(from: components)!
}

It comes down to how do you want do you want to treat 31 Dec 2018. Do you want it to appear in week 1 of 2019 (like the standard weekOfYear and yearForWeekOfYear does), or do you want to attribute it to “week 53 of 2018”.这归结为您想如何对待 2018 年 12 月 31 日。您希望它出现在 2019 年的第 1 周(就像标准的weekOfYearyearForWeekOfYear那样),还是想将其归因于“第 53 周” 2018 年”。 In short, do you want to treat the week starting Dec 30th 2018 as a single week (which the above will achieve), or do you want to split it up, treating the start of the week as week 53 of 2018 and the latter part of the week as week 1 of 2019?简而言之,您是想将 2018 年 12 月 30 日开始的那一周视为一个星期(以上将实现),还是要将其拆分,将这一周的开始视为 2018 年的第 53 周和后半周?作为 2019 年的第 1 周的一周?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM