// ByYear takes a Stream of DateCount instances and returns a Stream of // YearCount instances by totaling the counts in the DateCount instances // by year. The DateCount instances must be ordered by Date. Caller must Close // the returned Stream. Calling Close on returned Stream also closes s. func ByYear(s functional.Stream) functional.Stream { return functional.NewGenerator(func(e functional.Emitter) error { var ptr interface{} var opened bool var incoming DateCount // As soon as caller calls Close() we quit without pulling unnecessary // values from underlying stream. if ptr, opened = e.EmitPtr(); !opened { return s.Close() } var err error // We get the first date while propagating any errors to the caller. // Note that we stay in this loop until either we get a first date or // caller calls Close() for err = s.Next(&incoming); err != nil; err = s.Next(&incoming) { // Propagate error and yield execution to caller. Then get the next // pointer caller pases to Next(). if ptr, opened = e.Return(err); !opened { return s.Close() } } currentYear := incoming.Date.Year() // Running total for current year sum := incoming.Count // When Done marker is reached, we have to emit the count of the final year. for err = s.Next(&incoming); err != functional.Done; err = s.Next(&incoming) { // Propagate any errors to caller. if err != nil { if ptr, opened = e.Return(err); !opened { return s.Close() } continue } year := incoming.Date.Year() // If year changed, emit the count for the current year. // Then change currentYear and and make sum be the running total for // that year. if year != currentYear { *ptr.(*YearCount) = YearCount{Year: currentYear, Count: sum} if ptr, opened = e.Return(nil); !opened { return s.Close() } sum = incoming.Count currentYear = year } else { sum += incoming.Count } } // Emit the final year. *ptr.(*YearCount) = YearCount{Year: currentYear, Count: sum} // Note that we return nil, not functional.Done, since we are emitting the // count for the final year. The call to WaitForClose() takes // care of returning functional.Done to the caller until the caller calls // Close() e.Return(nil) functional.WaitForClose(e) return s.Close() }) }