Skip to content

Commit f6b1c68

Browse files
committed
Simplify mapDataPoints
1 parent 4dc2b49 commit f6b1c68

File tree

4 files changed

+122
-79
lines changed

4 files changed

+122
-79
lines changed

Modules/Sources/JetpackStats/Cards/ChartCardViewModel.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,8 @@ final class ChartCardViewModel: ObservableObject, TrafficCardViewModel {
183183
// Map previous data to align with current period dates so they
184184
// are displayed on the same timeline on the charts.
185185
let mappedPreviousDataPoints = DataPoint.mapDataPoints(
186-
previousDataPoints,
187-
from: dateRange.effectiveComparisonInterval,
188-
to: dateRange.dateInterval,
189-
component: dateRange.component,
190-
calendar: dateRange.calendar
186+
currentData: dataPoints,
187+
previousData: previousDataPoints
191188
)
192189

193190
output[metric] = ChartData(

Modules/Sources/JetpackStats/Cards/StandaloneChartCard.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,8 @@ private func generateChartData(
282282

283283
// Map previous data points to current period dates for overlay
284284
let mappedPreviousData = DataPoint.mapDataPoints(
285-
previousPeriod.dataPoints,
286-
from: previousDateInterval,
287-
to: dateRange.dateInterval,
288-
component: dateRange.component,
289-
calendar: calendar
285+
currentData: currentPeriod.dataPoints,
286+
previousData: previousPeriod.dataPoints
290287
)
291288

292289
return ChartData(

Modules/Sources/JetpackStats/Services/Data/DataPoint.swift

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,20 @@ struct DataPoint: Identifiable, Sendable {
1212

1313
extension DataPoint {
1414
/// Maps previous period data points to align with current period dates.
15+
/// Takes the dates from current data and replaces values with corresponding previous data values.
16+
/// Arrays are aligned from the end - if lengths differ, the beginning of the longer array is skipped.
1517
/// - Parameters:
16-
/// - previousData: The data points from the previous period
17-
/// - from: The date interval of the previous period
18-
/// - to: The date interval of the current period
19-
/// - component: The calendar component to use for date calculations
20-
/// - calendar: The calendar to use for date calculations
21-
/// - Returns: An array of data points with dates shifted to align with the current period
18+
/// - currentData: The data points from the current period (provides dates)
19+
/// - previousData: The data points from the previous period (provides values)
20+
/// - Returns: An array of data points with current dates and previous values
2221
static func mapDataPoints(
23-
_ dataPoits: [DataPoint],
24-
from: DateInterval,
25-
to: DateInterval,
26-
component: Calendar.Component,
27-
calendar: Calendar
22+
currentData: [DataPoint],
23+
previousData: [DataPoint]
2824
) -> [DataPoint] {
29-
let offset = calendar.dateComponents([component], from: from.start, to: to.start).value(for: component) ?? 0
30-
return dataPoits.map { dataPoint in
31-
DataPoint(
32-
date: calendar.date(byAdding: component, value: offset, to: dataPoint.date) ?? dataPoint.date,
33-
value: dataPoint.value
34-
)
35-
}
25+
// reversing to align by the last item in case there is a mismatch in the number of items
26+
zip(currentData.reversed(), previousData.reversed()).map { current, previous in
27+
DataPoint(date: current.date, value: previous.value)
28+
}.reversed()
3629
}
3730

3831
static func getTotalValue(for dataPoints: [DataPoint], metric: SiteMetric) -> Int? {

Modules/Tests/JetpackStatsTests/DataPointTests.swift

Lines changed: 107 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ import Foundation
44

55
@Suite
66
struct DataPointTests {
7-
let calendar = Calendar.mock(timeZone: .eastern)
8-
97
@Test("Maps previous data to current period with simple day offset")
108
func testMapPreviousDataToCurrentSimpleDayOffset() {
119
// GIVEN
12-
let previousStart = Date("2025-01-01T00:00:00-03:00")
13-
let previousEnd = Date("2025-01-08T00:00:00-03:00")
14-
let previousRange = DateInterval(start: previousStart, end: previousEnd)
15-
16-
let currentStart = Date("2025-01-08T00:00:00-03:00")
17-
let currentEnd = Date("2025-01-15T00:00:00-03:00")
18-
let currentRange = DateInterval(start: currentStart, end: currentEnd)
10+
let currentData = [
11+
DataPoint(date: Date("2025-01-08T00:00:00-03:00"), value: 0),
12+
DataPoint(date: Date("2025-01-09T00:00:00-03:00"), value: 0),
13+
DataPoint(date: Date("2025-01-10T00:00:00-03:00"), value: 0),
14+
DataPoint(date: Date("2025-01-11T00:00:00-03:00"), value: 0),
15+
DataPoint(date: Date("2025-01-12T00:00:00-03:00"), value: 0),
16+
DataPoint(date: Date("2025-01-13T00:00:00-03:00"), value: 0),
17+
DataPoint(date: Date("2025-01-14T00:00:00-03:00"), value: 0)
18+
]
1919

2020
let previousData = [
2121
DataPoint(date: Date("2025-01-01T00:00:00-03:00"), value: 100),
@@ -29,11 +29,8 @@ struct DataPointTests {
2929

3030
// WHEN
3131
let mappedData = DataPoint.mapDataPoints(
32-
previousData,
33-
from: previousRange,
34-
to: currentRange,
35-
component: .day,
36-
calendar: calendar
32+
currentData: currentData,
33+
previousData: previousData
3734
)
3835

3936
// THEN
@@ -49,13 +46,11 @@ struct DataPointTests {
4946
@Test("Maps previous month data to current month")
5047
func testMapPreviousMonthDataToCurrent() {
5148
// GIVEN
52-
let previousStart = Date("2024-12-01T00:00:00-03:00")
53-
let previousEnd = Date("2025-01-01T00:00:00-03:00")
54-
let previousRange = DateInterval(start: previousStart, end: previousEnd)
55-
56-
let currentStart = Date("2025-01-01T00:00:00-03:00")
57-
let currentEnd = Date("2025-02-01T00:00:00-03:00")
58-
let currentRange = DateInterval(start: currentStart, end: currentEnd)
49+
let currentData = [
50+
DataPoint(date: Date("2025-01-01T00:00:00-03:00"), value: 0),
51+
DataPoint(date: Date("2025-01-15T00:00:00-03:00"), value: 0),
52+
DataPoint(date: Date("2025-01-31T00:00:00-03:00"), value: 0)
53+
]
5954

6055
let previousData = [
6156
DataPoint(date: Date("2024-12-01T00:00:00-03:00"), value: 1000),
@@ -65,11 +60,8 @@ struct DataPointTests {
6560

6661
// WHEN
6762
let mappedData = DataPoint.mapDataPoints(
68-
previousData,
69-
from: previousRange,
70-
to: currentRange,
71-
component: .month,
72-
calendar: calendar
63+
currentData: currentData,
64+
previousData: previousData
7365
)
7466

7567
// THEN
@@ -85,39 +77,67 @@ struct DataPointTests {
8577
@Test("Maps with empty previous data")
8678
func testMapEmptyPreviousData() {
8779
// GIVEN
88-
let previousRange = DateInterval(
89-
start: Date("2025-01-01T00:00:00-03:00"),
90-
end: Date("2025-01-08T00:00:00-03:00")
91-
)
92-
let currentRange = DateInterval(
93-
start: Date("2025-01-08T00:00:00-03:00"),
94-
end: Date("2025-01-15T00:00:00-03:00")
95-
)
80+
let currentData = [
81+
DataPoint(date: Date("2025-01-08T00:00:00-03:00"), value: 100),
82+
DataPoint(date: Date("2025-01-09T00:00:00-03:00"), value: 200),
83+
DataPoint(date: Date("2025-01-10T00:00:00-03:00"), value: 300)
84+
]
9685
let previousData: [DataPoint] = []
9786

9887
// WHEN
9988
let mappedData = DataPoint.mapDataPoints(
100-
previousData,
101-
from: previousRange,
102-
to: currentRange,
103-
component: .day,
104-
calendar: calendar
89+
currentData: currentData,
90+
previousData: previousData
10591
)
10692

10793
// THEN
10894
#expect(mappedData.isEmpty)
10995
}
11096

97+
@Test("Maps with mismatched array lengths, aligned from the end")
98+
func testMapMismatchedArrayLengths() {
99+
// GIVEN - current has 6 items, previous has 5 items
100+
let currentData = [
101+
DataPoint(date: Date("2025-01-08T00:00:00-03:00"), value: 0),
102+
DataPoint(date: Date("2025-01-09T00:00:00-03:00"), value: 0),
103+
DataPoint(date: Date("2025-01-10T00:00:00-03:00"), value: 0),
104+
DataPoint(date: Date("2025-01-11T00:00:00-03:00"), value: 0),
105+
DataPoint(date: Date("2025-01-12T00:00:00-03:00"), value: 0),
106+
DataPoint(date: Date("2025-01-13T00:00:00-03:00"), value: 0)
107+
]
108+
109+
let previousData = [
110+
DataPoint(date: Date("2025-01-01T00:00:00-03:00"), value: 100),
111+
DataPoint(date: Date("2025-01-02T00:00:00-03:00"), value: 200),
112+
DataPoint(date: Date("2025-01-03T00:00:00-03:00"), value: 300),
113+
DataPoint(date: Date("2025-01-04T00:00:00-03:00"), value: 400),
114+
DataPoint(date: Date("2025-01-05T00:00:00-03:00"), value: 500)
115+
]
116+
117+
// WHEN
118+
let mappedData = DataPoint.mapDataPoints(
119+
currentData: currentData,
120+
previousData: previousData
121+
)
122+
123+
// THEN - should have 5 items (min count), aligned from the end
124+
// current[1..5] paired with previous[0..4]
125+
#expect(mappedData.count == 5)
126+
#expect(mappedData[0].date == Date("2025-01-09T00:00:00-03:00"))
127+
#expect(mappedData[0].value == 100)
128+
#expect(mappedData[1].date == Date("2025-01-10T00:00:00-03:00"))
129+
#expect(mappedData[1].value == 200)
130+
#expect(mappedData[4].date == Date("2025-01-13T00:00:00-03:00"))
131+
#expect(mappedData[4].value == 500)
132+
}
133+
111134
@Test("Maps year-over-year comparison")
112135
func testMapYearOverYearComparison() {
113136
// GIVEN
114-
let previousStart = Date("2024-01-01T00:00:00-03:00")
115-
let previousEnd = Date("2024-01-08T00:00:00-03:00")
116-
let previousRange = DateInterval(start: previousStart, end: previousEnd)
117-
118-
let currentStart = Date("2025-01-01T00:00:00-03:00")
119-
let currentEnd = Date("2025-01-08T00:00:00-03:00")
120-
let currentRange = DateInterval(start: currentStart, end: currentEnd)
137+
let currentData = [
138+
DataPoint(date: Date("2025-01-01T00:00:00-03:00"), value: 0),
139+
DataPoint(date: Date("2025-01-07T00:00:00-03:00"), value: 0)
140+
]
121141

122142
let previousData = [
123143
DataPoint(date: Date("2024-01-01T00:00:00-03:00"), value: 1000),
@@ -126,11 +146,8 @@ struct DataPointTests {
126146

127147
// WHEN
128148
let mappedData = DataPoint.mapDataPoints(
129-
previousData,
130-
from: previousRange,
131-
to: currentRange,
132-
component: .year,
133-
calendar: calendar
149+
currentData: currentData,
150+
previousData: previousData
134151
)
135152

136153
// THEN
@@ -140,4 +157,43 @@ struct DataPointTests {
140157
#expect(mappedData[1].date == Date("2025-01-07T00:00:00-03:00"))
141158
#expect(mappedData[1].value == 2000)
142159
}
160+
161+
@Test("Maps previous week data to current week")
162+
func testMapPreviousWeekDataToCurrent() {
163+
// GIVEN
164+
let currentData = [
165+
DataPoint(date: Date("2025-11-17T05:00:00+00:00"), value: 0),
166+
DataPoint(date: Date("2025-11-24T05:00:00+00:00"), value: 0),
167+
DataPoint(date: Date("2025-12-01T05:00:00+00:00"), value: 0),
168+
DataPoint(date: Date("2025-12-08T05:00:00+00:00"), value: 0),
169+
DataPoint(date: Date("2025-12-15T05:00:00+00:00"), value: 0)
170+
]
171+
172+
let previousData = [
173+
DataPoint(date: Date("2025-10-20T04:00:00+00:00"), value: 3),
174+
DataPoint(date: Date("2025-10-27T04:00:00+00:00"), value: 5),
175+
DataPoint(date: Date("2025-11-03T05:00:00+00:00"), value: 0),
176+
DataPoint(date: Date("2025-11-10T05:00:00+00:00"), value: 0),
177+
DataPoint(date: Date("2025-11-17T05:00:00+00:00"), value: 1)
178+
]
179+
180+
// WHEN
181+
let mappedData = DataPoint.mapDataPoints(
182+
currentData: currentData,
183+
previousData: previousData
184+
)
185+
186+
// THEN
187+
#expect(mappedData.count == 5)
188+
#expect(mappedData[0].date == Date("2025-11-17T05:00:00+00:00"))
189+
#expect(mappedData[0].value == 3)
190+
#expect(mappedData[1].date == Date("2025-11-24T05:00:00+00:00"))
191+
#expect(mappedData[1].value == 5)
192+
#expect(mappedData[2].date == Date("2025-12-01T05:00:00+00:00"))
193+
#expect(mappedData[2].value == 0)
194+
#expect(mappedData[3].date == Date("2025-12-08T05:00:00+00:00"))
195+
#expect(mappedData[3].value == 0)
196+
#expect(mappedData[4].date == Date("2025-12-15T05:00:00+00:00"))
197+
#expect(mappedData[4].value == 1)
198+
}
143199
}

0 commit comments

Comments
 (0)