11unit WeatherStation;
22
33{ $mode objfpc}{ $H+}{ $J-}{ $modeSwitch advancedRecords}
4-
54interface
65
76uses
87 { $IFDEF UNIX}
9- cthreads,
8+ cmem, cthreads,
109 { $ENDIF}
1110 Classes,
1211 SysUtils,
@@ -21,15 +20,16 @@ interface
2120 // Create a record of temperature stats
2221 TStat = record
2322 var
24- min: int64;
25- max: int64;
23+ min: int64; // Borrowed the concept from go's approach to improve performance, save floats as int64
24+ max: int64; // This saved ~2 mins processing time.
2625 sum: int64;
27- Count : int64;
26+ cnt : int64;
2827 private
2928 function RoundEx (x: double): double; // Borrowed from the baseline program
3029 function PascalRound (x: double): double; // Borrowed from the baseline program
3130 public
32- constructor Create(newMin: int64; newMax: int64; newSum: int64; newCount: int64);
31+ constructor Create(const newMin: int64; const newMax: int64;
32+ const newSum: int64; const newCount: int64);
3333 function ToString : string;
3434
3535 end ;
@@ -39,8 +39,7 @@ TStat = record
3939 TWeatherDictionary = specialize TDictionary<string, TStat>;
4040
4141// The main algorithm to process the temp measurements from various weather station
42- procedure ProcessTempMeasurements (filename: string);
43-
42+ procedure ProcessTempMeasurements (const filename: string);
4443
4544implementation
4645
@@ -70,29 +69,24 @@ function TStat.PascalRound(x: double): double;
7069 Result := t;
7170end ;
7271
73- constructor TStat.Create(newMin: int64; newMax: int64; newSum: int64; newCount: int64);
72+ constructor TStat.Create(const newMin: int64; const newMax: int64;
73+ const newSum: int64; const newCount: int64);
7474begin
7575 self.min := newMin;
7676 self.max := newMax;
7777 self.sum := newSum;
78- self.Count := newCount;
78+ self.cnt := newCount;
7979end ;
8080
8181function TStat.ToString : string;
8282var
8383 minR, meanR, maxR: double; // Store the rounded values prior saving to TStringList.
8484begin
85- { $IFDEF DEBUG}
86- Result := Format(' Min: %.1f; Mean: %.1f; Maxp: %.1f; Sum: %.1f; Count %d' ,
87- [self.min, (self.sum / self.Count), self.max,
88- self.sum, self.Count]);
89- { $ENDIF DEBUG}
90- // Result := Format('%.1f/%.1f/%.1f', [self.min, (self.sum / self.count), self.max]);
9185 minR := RoundEx(self.min / 10 );
9286 maxR := RoundEx(self.max / 10 );
93- meanR := RoundEx(self.sum / self.Count / 10 );
94- Result := FormatFloat(' 0.0' , minR) + ' /' + FormatFloat(' 0.0' , meanR) + ' / ' + FormatFloat( ' 0.0 ' , maxR);
95-
87+ meanR := RoundEx(self.sum / self.cnt / 10 );
88+ Result := FormatFloat(' 0.0' , minR) + ' /' + FormatFloat(' 0.0' , meanR) +
89+ ' / ' + FormatFloat( ' 0.0 ' , maxR);
9690end ;
9791
9892{
@@ -121,18 +115,14 @@ function CustomTStringListComparer(AList: TStringList;
121115end ;
122116
123117
124- procedure AddCityTemperature (cityName: string; newTemp: int64; var weatherDictionary: TWeatherDictionary);
118+ procedure AddCityTemperature (const cityName: string; const newTemp: int64;
119+ var weatherDictionary: TWeatherDictionary);
125120var
126121 stat: TStat;
127122begin
128- // If city name exists , modify temp as needed
123+ // If city name esxists , modify temp as needed
129124 if weatherDictionary.ContainsKey(cityName) then
130125 begin
131-
132- { $IFDEF DEBUG}
133- WriteLn(' City found: ' , cityName);
134- { $ENDIF DEBUG}
135-
136126 // Get the temp record
137127 stat := weatherDictionary[cityName];
138128
@@ -146,7 +136,7 @@ procedure AddCityTemperature(cityName: string; newTemp: int64; var weatherDictio
146136 stat.sum := stat.sum + newTemp;
147137
148138 // Increase the counter
149- stat.Count := stat.Count + 1 ;
139+ stat.cnt := stat.cnt + 1 ;
150140
151141 // Update the stat of this city
152142 weatherDictionary.AddOrSetValue(cityName, stat);
@@ -156,27 +146,20 @@ procedure AddCityTemperature(cityName: string; newTemp: int64; var weatherDictio
156146 if not weatherDictionary.ContainsKey(cityName) then
157147 begin
158148 weatherDictionary.Add(cityName, TStat.Create(newTemp, newTemp, newTemp, 1 ));
159- { $IFDEF DEBUG}
160- WriteLn(' Added: ' , cityName);
161- { $ENDIF DEBUG}
162149 end ;
163150end ;
164151
165- procedure ProcessTempMeasurements (filename: string);
152+ procedure ProcessTempMeasurements (const filename: string);
166153var
167154 wd: TWeatherDictionary;
168- line, ws, recordedTemp : string;
155+ line, ws, strTemp : string;
169156 weatherStationList: TStringList;
170157 textFile: System.TextFile;
171- isFirstKey: boolean = True;
172- delimiterPos: integer;
158+ delimiterPos, valCode: integer;
159+ intTemp: int64;
160+ index: integer;
173161begin
174162
175- // Start a timer
176- { $IFDEF DEBUG}
177- Stopwatch.StartTimer;
178- { $ENDIF}
179-
180163 // Create a city - weather dictionary
181164 wd := TWeatherDictionary.Create;
182165 weatherStationList := TStringList.Create;
@@ -190,32 +173,36 @@ procedure ProcessTempMeasurements(filename: string);
190173 // Open the file for reading
191174 Reset(textFile);
192175
176+ { $IFDEF DEBUG}
177+ // Start a timer
178+ Stopwatch.StartTimer;
179+ { $ENDIF}
180+
193181 // Keep reading lines until the end of the file is reached
194182 while not EOF(textFile) do
195183 begin
196184 // Read a line
197185 ReadLn(textFile, line);
198186
199- // If the line start with #, then continue/skip.
200- if (line[1 ] = ' #' ) then continue;
201-
202187 // Get position of the delimiter
203188 delimiterPos := Pos(' ;' , line);
204-
205- // Else, add an entry into the dictionary.
206- // Get the weather station name
207- // Using Copy ans POS - as suggested by Gemini AI.
208- // This made the program 3 mins fater when processing 1 billion rows.
209- ws := Copy(line, 1 , delimiterPos - 1 );
210-
211- // Get the temperature recorded, as string, remove '.' from string float
212- // because we want to save it as int64.
213- recordedTemp := Copy(line, delimiterPos + 1 , Length(line));
214- recordedTemp := StringReplace(recordedTemp, ' .' , ' ' , [rfReplaceAll, rfIgnoreCase]);
215-
216- // Add the weather station and the recorded temp (as int64) in the TDictionary
217- AddCityTemperature(ws, StrToInt64(recordedTemp), wd);
218-
189+ if delimiterPos > 0 then
190+ begin
191+ // Get the weather station name
192+ // Using Copy and POS - as suggested by Gemini AI.
193+ // This part saves 3 mins faster when processing 1 billion rows.
194+ ws := Copy(line, 1 , delimiterPos - 1 );
195+
196+ // Get the temperature recorded, as string, remove '.' from string float
197+ // because we want to save it as int64.
198+ strTemp := Copy(line, delimiterPos + 1 , Length(line));
199+ strTemp := StringReplace(strTemp, ' .' , ' ' , [rfReplaceAll]);
200+
201+ // Add the weather station and the recorded temp (as int64) in the TDictionary
202+ Val(strTemp, intTemp, valCode);
203+ if valCode <> 0 then Continue;
204+ AddCityTemperature(ws, intTemp, wd);
205+ end ;
219206 end ; // end while loop reading line at a time
220207
221208 // Close the file
@@ -226,46 +213,52 @@ procedure ProcessTempMeasurements(filename: string);
226213 WriteLn(' File handling error occurred. Details: ' , E.Message);
227214 end ; // End of file reading ////////////////////////////////////////////////
228215
216+ { $IFDEF DEBUG}
217+ Stopwatch.StopTimer;
218+ WriteLn(' Finished reading and updating dictionary' );
219+ Stopwatch.DisplayTimer;
220+ { $ENDIF}
221+
229222 // Format and sort weather station by name and temp stat ///////////////////
230- ws:=' ' ;
223+ { $IFDEF DEBUG}
224+ Stopwatch.StartTimer;
225+ { $ENDIF}
226+ ws := ' ' ;
231227 for ws in wd.Keys do
232228 begin
233- weatherStationList.Add(ws + ' =' + wd[ws].ToString);
229+ weatherStationList.Add(ws + ' =' + wd[ws].ToString + ' , ' );
234230 end ;
235231 weatherStationList.CustomSort(@CustomTStringListComparer);
236232
237- // Print TStringList - sorted by weather station and temp stat /////////////
238- Write(' {' );
239- for ws in weatherStationList do
240- begin
241- // If it's not the first key, print a comma
242- if not isFirstKey then
243- Write(' , ' );
244-
245- // Print the weather station and the temp stat
246- Write(ws);
247-
248- // Set isFirstKey to False after printing the first key
249- isFirstKey := False;
250- end ;
233+ { $IFDEF DEBUG}
234+ Stopwatch.StopTimer;
235+ WriteLn(' Finished creating TStringList and sorted it' );
236+ Stopwatch.DisplayTimer;
237+ { $ENDIF}
251238
252- WriteLn(' }' );
239+ // Print TStringList - sorted by weather station and temp stat /////////////
240+ { $IFDEF DEBUG}
241+ Stopwatch.StartTimer;
242+ { $ENDIF}
243+ strTemp := ' ' ;
244+ // Print the weather station and the temp stat
245+ for index := 0 to weatherStationList.Count - 1 do
246+ strTemp := strTemp + weatherStationList[index];
247+ // Remove last comma and space; ', ', a neat trick from Gus.
248+ SetLength(strTemp, Length(strTemp) - 2 );
249+ WriteLn(' {' , strTemp, ' }' );
253250
254251 { $IFDEF DEBUG}
255- WriteLn(' DEBUG mode on' );
256- { $ENDIF DEBUG}
252+ Stopwatch.StopTimer;
253+ WriteLn(' Finished printing the sorted weather station and temperatures' );
254+ Stopwatch.DisplayTimer;
255+ { $ENDIF}
257256
258257 finally
259258 weatherStationList.Free;
260259 wd.Free;
261260 end ; // End of processing TDictionary and TStringList
262261
263- // Stop a timer
264- { $IFDEF DEBUG}
265- Stopwatch.StopTimer;
266- Stopwatch.DisplayTimer;
267- { $ENDIF}
268-
269262end ;
270263
271264end .
0 commit comments