@rasifix/orienteering-utils 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # Description
2
2
  Library functions for orienteering split analyzis. Currently supported file
3
- formats are Kraemer and Oware.
3
+ formats are Kraemer, Solv and Oware.
@@ -0,0 +1,25 @@
1
+ const { parseTime, formatTime } = require('./time');
2
+
3
+ export function errorTime(runner, options) {
4
+ let thresholdRelative = options.thresholdRelative || 1.2;
5
+ let thresholdAbsolute = options.thresholdAbsolute || 10;
6
+
7
+ let perfindices = runner.splits.filter(perfidx => perfidx).map(split => split.perfidx).sort((s1, s2) => s1 - s2 );
8
+ let middle = null;
9
+ if (perfindices.length % 2 === 1) {
10
+ middle = perfindices[Math.floor(perfindices.length / 2)];
11
+ } else {
12
+ middle = (perfindices[perfindices.length / 2] + perfindices[perfindices.length / 2 + 1]) / 2;
13
+ }
14
+
15
+ let errorTime = 0;
16
+ runner.splits.filter(split => split.split !== '-' && split.split !== 's').forEach(split => {
17
+ let errorFreeTime = Math.round(parseTime(split.split) * (split.perfidx / middle));
18
+ if (parseTime(split.split) / errorFreeTime > thresholdRelative && (parseTime(split.split) - errorFreeTime) > thresholdAbsolute) {
19
+ split.timeLoss = formatTime(parseTime(split.split) - errorFreeTime);
20
+ errorTime += parseTime(split.timeLoss);
21
+ }
22
+ });
23
+
24
+ return formatTime(errorTime);
25
+ }
@@ -0,0 +1,59 @@
1
+ function extractCourses(category) {
2
+ var courses = { };
3
+ category.runners.forEach(function(runner) {
4
+ const course = runner.splits.map(function(split, index, splits) {
5
+ return split.code;
6
+ });
7
+ const key = 'St-' + course.join('-');
8
+ if (!courses[key]) {
9
+ courses[key] = {
10
+ runners: [],
11
+ controls: course.map(function(c) {
12
+ return { code: c };
13
+ })
14
+ };
15
+ }
16
+ courses[key].runners.push(runner);
17
+ });
18
+ const coursesArray = Object.keys(courses).map(function(course) {
19
+ return courses[course];
20
+ });
21
+
22
+ for (var i = 0; i < coursesArray[0].controls.length; i++) {
23
+ const base = coursesArray[0].controls[i].code;
24
+ const shared = coursesArray.every(function(course) {
25
+ return course.controls[i].code === base;
26
+ });
27
+ coursesArray.forEach(function(course) {
28
+ course.controls[i].shared = shared;
29
+ });
30
+ }
31
+
32
+ return coursesArray;
33
+ };
34
+
35
+ module.exports.extractCourses = extractCourses;
36
+
37
+ /**
38
+ * A category is classified as "fair" if all the runners have the exact same legs, although
39
+ * potentially in different order.
40
+ */
41
+ module.exports.isFair = function(category) {
42
+ const courses = extractCourses(category);
43
+ const courseLegs = courses.map(function(course) {
44
+ const legs = [];
45
+ for (var i = 0; i < course.controls.length - 1; i++) {
46
+ const from = course.controls[i].code;
47
+ const to = course.controls[i + 1].code;
48
+ legs.push(from + '-' + to);
49
+ }
50
+ return legs.sort().join(':');
51
+ });
52
+
53
+ const grouped = { };
54
+ courseLegs.forEach(function(course) {
55
+ grouped[course] = true;
56
+ });
57
+
58
+ return Object.keys(grouped).length === 1;
59
+ };
package/lib/oware.js CHANGED
@@ -8,15 +8,15 @@ function parseCategory(row) {
8
8
  };
9
9
  }
10
10
 
11
- function parseRunner(row) {
11
+ function parseRunner(row, category, id) {
12
12
  var headerLength = 15;
13
13
  var i;
14
-
14
+
15
15
  var splits = [];
16
16
  for (i = headerLength; i < row.length; i += 2) {
17
17
  splits.push({ 'code': row[i], 'time': row[i + 1] });
18
18
  }
19
-
19
+
20
20
  // split cleanup - detect two following splits with identical time
21
21
  // --> control not working properly; set 's' as split time (substitute)
22
22
  // going from back to front to catch several not working controls
@@ -25,8 +25,10 @@ function parseRunner(row) {
25
25
  splits[i].time = 's';
26
26
  }
27
27
  }
28
-
28
+
29
29
  return {
30
+ id: id,
31
+ category: category,
30
32
  rank: row[0] ? parseInt(row[0]) : null,
31
33
  name: row[1],
32
34
  firstName: row[2],
@@ -49,10 +51,10 @@ module.exports.parse = function(text) {
49
51
 
50
52
  // throw away first row containing headers
51
53
  lines = lines.splice(1);
52
-
54
+
53
55
  // second row contains information about the event
54
56
  var header = lines[0].split(';');
55
-
57
+
56
58
  var event = {
57
59
  // row starts with a double slash
58
60
  name: header[0].substring(2, header[0].length),
@@ -61,22 +63,23 @@ module.exports.parse = function(text) {
61
63
  startTime: header[3],
62
64
  categories: [ ]
63
65
  };
64
-
66
+
65
67
  // throw a way the now parsed header
66
68
  lines = lines.splice(1);
67
-
69
+
68
70
  var category = null;
69
-
71
+
72
+ let idx = 0;
70
73
  lines.filter(line => line.trim().length > 0).forEach(function(line) {
71
74
  var cols = line.split(';');
72
75
  if (cols.length === 4) {
73
76
  category = parseCategory(cols);
74
77
  event.categories.push(category);
75
78
  } else {
76
- category.runners.push(parseRunner(cols));
79
+ category.runners.push(parseRunner(cols, category.name, ++idx));
77
80
  }
78
- });
79
-
81
+ });
82
+
80
83
  return event;
81
84
  };
82
85
 
package/lib/ranking.js ADDED
@@ -0,0 +1,422 @@
1
+ const { parseTime, formatTime } = require('./time');
2
+ const { reorganize } = require('./reorganize');
3
+
4
+
5
+ function invalidTime(time) {
6
+ return !time || time < 0;
7
+ }
8
+
9
+ function validTime(time) {
10
+ return !invalidTime(time);
11
+ }
12
+
13
+ function sum(a1, a2) {
14
+ return a1 + a2;
15
+ }
16
+
17
+ function defineCourses(runners) {
18
+ let courses = { };
19
+ runners.forEach(runner => {
20
+ let course = defineCourse(runner);
21
+ if (!courses[course]) {
22
+ courses[course] = {
23
+ code: course,
24
+ runners: [ runner.id ]
25
+ };
26
+ } else {
27
+ courses[course].runners.push(runner.id);
28
+ }
29
+ });
30
+ return Object.keys(courses).map(key => courses[key]);
31
+ }
32
+
33
+ function defineCourse(runner) {
34
+ return 'St,' + runner.splits.map(split => split.code).join(',');
35
+ }
36
+
37
+ function defineLegs(runners) {
38
+ let result = { };
39
+
40
+ runners.filter(runner => runner.splits.length > 0).forEach(runner => {
41
+ runner.splits.forEach((split, idx) => {
42
+ if (validTime(split.split)) {
43
+ let from = idx === 0 ? 'St' : runner.splits[idx - 1].code;
44
+ let code = from + '-' + split.code;
45
+ addRunnerToLeg(result, code, runner, split.split);
46
+ }
47
+ });
48
+ });
49
+
50
+ return result;
51
+ }
52
+
53
+ function addRunnerToLeg(legs, code, runner, split) {
54
+ let leg = legs[code];
55
+ if (!leg) {
56
+ leg = legs[code] = createLeg(code);
57
+ }
58
+
59
+ leg.runners.push({
60
+ id: runner.id,
61
+ fullName: runner.fullName,
62
+ split: split
63
+ });
64
+ }
65
+
66
+ function createLeg(code) {
67
+ return {
68
+ code: code,
69
+ runners: []
70
+ };
71
+ }
72
+
73
+ function defineRunners(runners) {
74
+ return runners.map(function(runner) {
75
+ return {
76
+ id: runner.id,
77
+ fullName: runner.fullName,
78
+ time: runner.time,
79
+ yearOfBirth: runner.yearOfBirth,
80
+ city: runner.city,
81
+ club: runner.club,
82
+ splits: runner.splits.map(function(split, idx) {
83
+ var splitTime = null;
84
+ if (split.time === '-') {
85
+ splitTime = '-';
86
+ } else if (idx === 0) {
87
+ splitTime = parseTime(split.time);
88
+ } else {
89
+ if (parseTime(split.time) === null || parseTime(runner.splits[idx - 1].time) === null) {
90
+ splitTime = '-';
91
+ } else {
92
+ splitTime = parseTime(split.time) - parseTime(runner.splits[idx - 1].time);
93
+ }
94
+ }
95
+
96
+ return {
97
+ code: split.code,
98
+ time: split.time,
99
+ split: splitTime
100
+ };
101
+ })
102
+ };
103
+ });
104
+ }
105
+
106
+ /**
107
+ * Define properties of each leg. After this method the leg structure will be enhanced as follows:
108
+ *
109
+ * - runners are sorted per split time per leg
110
+ * - each leg has a property 'idealSplit' (ideal split time of this leg)
111
+ * - each leg has a property 'fastestSplit'
112
+ * - each runner entry of a leg is enhanced with 'splitBehind' and 'splitRank'
113
+ *
114
+ * @param {*} legs leg data structre (only split is relevant)
115
+ */
116
+ function defineLegProperties(legs) {
117
+ Object.keys(legs).forEach(code => {
118
+ let leg = legs[code];
119
+ leg.runners.sort(function(r1, r2) {
120
+ return r1.split - r2.split;
121
+ });
122
+
123
+ // calculate the ideal time: take up to 5 fastest on that leg
124
+ let selected = leg.runners.slice(0, Math.min(leg.runners.length, 5)).map(runner => runner.split);
125
+
126
+ // only if there are valid splits for this leg
127
+ if (selected.length > 0) {
128
+ leg.idealSplit = Math.round(selected.reduce(sum) / selected.length);
129
+ }
130
+
131
+ // only if there are valid splits for this leg
132
+ if (leg.runners.length > 0) {
133
+ leg.fastestSplit = leg.runners[0].split;
134
+ leg.runners[0].splitBehind = '0:00';
135
+ leg.runners.slice(1).forEach(runner => {
136
+ runner.splitBehind = runner.split - leg.fastestSplit;
137
+ });
138
+
139
+ leg.runners[0].splitRank = 1;
140
+ leg.runners.forEach((runner, idx, arr) => {
141
+ if (idx > 0) {
142
+ if (runner.split === arr[idx - 1].split) {
143
+ runner.splitRank = arr[idx - 1].splitRank;
144
+ } else {
145
+ runner.splitRank = idx + 1;
146
+ }
147
+ runner.performanceIndex = Math.round(1.0 * leg.idealSplit / runner.split * 100);
148
+ }
149
+ });
150
+ }
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Calculates the weight of each leg regarding the passed in idealTime.
156
+ *
157
+ * @param {*} legs
158
+ * @param {*} idealTime
159
+ */
160
+ function defineLegWeight(legs, idealTime) {
161
+ Object.keys(legs).forEach((code) => {
162
+ let leg = legs[code];
163
+ leg.weight = leg.idealSplit / idealTime;
164
+ });
165
+ }
166
+
167
+ function legCode(splits, idx) {
168
+ if (idx === 0) {
169
+ return 'St-' + splits[0].code;
170
+ } else {
171
+ return splits[idx - 1].code + '-' + splits[idx].code;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Assigns the following property to every valid leg of a runner.
177
+ *
178
+ * - splitBehind: how much time behind the faster runner
179
+ * - splitRank: rank on corresponding leg
180
+ * - leg: code of leg
181
+ *
182
+ * @param {*} runners
183
+ * @param {*} legs
184
+ */
185
+ function assignLegInfoToSplits(runners, legs) {
186
+ runners.forEach(runner => {
187
+ runner.splits.filter(s => validTime(s.split)).forEach((split, idx) => {
188
+ let code = legCode(runner.splits, idx);
189
+ let leg = legs[code];
190
+
191
+ if (!leg) {
192
+ throw 'leg with code ' + code + ' not defined!';
193
+ }
194
+
195
+ split.leg = leg.code;
196
+
197
+ let legRunner = leg.runners.find(r => r.id === runner.id);
198
+
199
+ split.idealBehind = legRunner ? legRunner.split - leg.idealSplit : '-';
200
+ split.supermanBehind = legRunner ? legRunner.split - leg.fastestSplit : '-';
201
+ split.splitBehind = legRunner ? legRunner.splitBehind : '-';
202
+ split.splitRank = legRunner ? legRunner.splitRank : null;
203
+ split.performanceIndex = legRunner ? legRunner.performanceIndex : null;
204
+ split.weight = leg.weight;
205
+ });
206
+ });
207
+ }
208
+
209
+ function rank(runners) {
210
+ runners.forEach((runner, idx) => {
211
+ if (idx === 0) {
212
+ runner.rank = 1;
213
+ } else {
214
+ let prev = runners[idx - 1];
215
+ if (prev.time === runner.time) {
216
+ runner.rank = prev.rank;
217
+ } else if (parseTime(runner.time)) {
218
+ runner.rank = idx + 1;
219
+ }
220
+ }
221
+ });
222
+ }
223
+
224
+ class RankingBuilder {
225
+
226
+ constructor(runners) {
227
+ runners.forEach((runner, idx) => {
228
+ for (let i = 0; i < runner.splits.length; i++) {
229
+ let split = runner.splits[i];
230
+ if (i === 0 && validTime(split.time)) {
231
+ split.split = parseTime(split.time);
232
+ } else if (i > 0) {
233
+ let previousSplit = runner.splits[i - 1];
234
+ if (validTime(split.time) && validTime(previousSplit.time)) {
235
+ split.split = parseTime(split.time) - parseTime(previousSplit.time);
236
+ }
237
+ }
238
+ }
239
+ });
240
+
241
+ this.courses = defineCourses(runners);
242
+ this.legs = defineLegs(runners);
243
+ this.runners = defineRunners(runners);
244
+
245
+ defineLegProperties(this.legs);
246
+
247
+ // calculate the ideal time [s]
248
+ this.idealTime = Object.keys(this.legs).map(code => this.legs[code].idealSplit).reduce(sum);
249
+
250
+ // each leg's weight is calculated regarding as a ratio of the ideal split time to the ideal time
251
+ defineLegWeight(this.legs, this.idealTime);
252
+
253
+ // now assing the leg information (such as idealTime, weight, ...) to the individual splits of the runners
254
+ assignLegInfoToSplits(this.runners, this.legs);
255
+
256
+
257
+ this.runners.forEach(runner => {
258
+ let behind = 0;
259
+ let supermanBehind = 0;
260
+ let weightSum = 0;
261
+ runner.splits.forEach(split => {
262
+ behind += split.idealBehind;
263
+ supermanBehind += split.supermanBehind;
264
+ split.overallIdealBehind = behind;
265
+ split.overallSupermanBehind = supermanBehind;
266
+ split.position = weightSum;
267
+
268
+ weightSum += split.weight;
269
+ });
270
+ });
271
+
272
+ // function of function calculating the time at an arbitrary position for a given runner
273
+ let timeFn = (runner) => (pos) => {
274
+ if (pos === 0) {
275
+ return 0;
276
+ } else if (pos >= 1) {
277
+ return parseTime(runner.time);
278
+ }
279
+
280
+ let idx = 0;
281
+ let weightSum = 0;
282
+ for (idx = 0; idx < runner.splits.length; idx++) {
283
+ let split = runner.splits[idx];
284
+ if (weightSum + split.weight > pos) {
285
+ break;
286
+ }
287
+ weightSum += split.weight;
288
+ }
289
+
290
+ let prev = runner.splits[idx - 1];
291
+ let next = runner.splits[idx];
292
+
293
+ let prevTime = parseTime(prev.time);
294
+ let nextTime = parseTime(next.time);
295
+ return prevTime + (pos - prev.position) / (next.position - prev.position) * (nextTime - prevTime);
296
+ };
297
+
298
+ let timesAtPosition = (pos) => {
299
+ return this.runners.map(runner => { return { id: runner.id, time: timeFn(runner)(pos) }; });
300
+ };
301
+
302
+ this.runners.forEach(runner => {
303
+ runner.splits.forEach(split => {
304
+ let times = timesAtPosition(split.position + split.weight).filter(entry => entry.time !== null && entry.time > 0);
305
+ times.sort((t1, t2) => t1.time - t2.time);
306
+
307
+ let rank = 1;
308
+ let fastestTime = times[0].time;
309
+
310
+ let lastTime = parseTime(times[0].time);
311
+ for (let idx = 0; idx < times.length; idx++) {
312
+ let entry = times[idx];
313
+
314
+ if (lastTime < parseTime(entry.time)) {
315
+ rank = idx;
316
+ }
317
+
318
+ if (runner.id === entry.id) {
319
+ split.overallRank = rank;
320
+ split.overallBehind = formatTime(entry.time - fastestTime);
321
+ break;
322
+ }
323
+ }
324
+ });
325
+ });
326
+
327
+ console.log(this.runners[1]);
328
+
329
+ // calculate the overall rank
330
+ rank(this.runners);
331
+ }
332
+
333
+ getCourses() {
334
+ return this.courses;
335
+ }
336
+
337
+ getLeg(code) {
338
+ return this.legs[code];
339
+ }
340
+
341
+ getLegs() {
342
+ return Object.keys(this.legs).map(code => this.legs[code]);
343
+ }
344
+
345
+ getRanking() {
346
+ let runners = this.runners.map(runner => { return { ...runner } });
347
+
348
+
349
+ /*
350
+
351
+ for each runner we need the following function
352
+
353
+ f(pos) => timeBehindIdeal
354
+
355
+ using this function it will be possible to calculate the following properties
356
+
357
+ f(runner, split) => {
358
+ overallRank // the rank the current position
359
+ idealBehind // time behind ideal
360
+ }
361
+
362
+ the first function
363
+
364
+ */
365
+ }
366
+ }
367
+
368
+ module.exports.RankingBuilder = RankingBuilder;
369
+
370
+
371
+ /*
372
+ /* depends on ordered courses!
373
+ Object.keys(results.legs).forEach((code, idx) => {
374
+ let leg = result.legs[code];
375
+ if (idx === 0) {
376
+ leg.fastestTime = formatTime(leg.fastestSplit);
377
+ leg.idealTime = formatTime(leg.idealSplit);
378
+ } else {
379
+ leg.fastestTime = formatTime(parseTime(result.legs[idx - 1].fastestTime) + leg.fastestSplit);
380
+ leg.idealTime = formatTime(parseTime(result.legs[idx - 1].idealTime) + leg.idealSplit);
381
+ }
382
+ });
383
+
384
+ result.runners.forEach(function(runner) {
385
+ // calculate overall time behind leader
386
+ runner.splits.forEach(function(split, splitIdx) {
387
+ if (!invalidTime(split.time)) {
388
+ let leader = result.runners.map(function(r) {
389
+ return {
390
+ time: r.splits[splitIdx].time,
391
+ rank: r.splits[splitIdx].overallRank
392
+ };
393
+ }).find(function(split) {
394
+ return split.rank === 1;
395
+ });
396
+
397
+ // no leader for this leg?!
398
+ if (leader) {
399
+ let leaderTime = leader.time;
400
+ if (parseTime(split.time) !== null) {
401
+ split.overallBehind = formatTime(parseTime(split.time) - parseTime(leaderTime));
402
+ split.fastestBehind = formatTime(parseTime(split.time) - parseTime(result.legs[splitIdx].fastestTime));
403
+ split.idealBehind = formatTime(parseTime(split.time) - parseTime(result.legs[splitIdx].idealTime));
404
+ } else {
405
+ split.overallBehind = '-';
406
+ split.fastestBehind = '-';
407
+ split.idealBehind = '-';
408
+ }
409
+ }
410
+ }
411
+ });
412
+
413
+ // performance index for split
414
+ runner.splits.filter(split => split.split != '-' && split.split != 's' && split.split != '0:00').forEach(split => {
415
+ let leg = findLeg(legs, split.leg);
416
+ split.perfidx = Math.round(1.0 * leg.idealSplit / parseTime(split.split) * 100);
417
+ });
418
+ });
419
+
420
+ return result;
421
+ }
422
+ */
package/lib/reorganize.js CHANGED
@@ -1,14 +1,83 @@
1
1
  const { parseTime, formatTime } = require('./time');
2
2
  const assert = require('assert');
3
3
 
4
- module.exports.reorganize = function(runner, course) {
5
- let time = 0;
6
- let splits = [];
7
- for (let idx = 0; idx < course.length; idx++) {
8
- let from = idx === 0 ? 'St' : course[idx];
9
- let to = idx
4
+ module.exports.reorganize = function(runners, course) {
5
+ return runners.filter(r => parseTime(r.time) !== null).map(r => {
6
+ let runner = { ...r };
7
+ let runnerCourse = 'St,' + runner.splits.map(s => s.code).join(',');
8
+ console.log(runner.fullName, runnerCourse);
9
+ if (runnerCourse === course.join(',')) {
10
+ return runner;
11
+ }
12
+
13
+ let time = 0;
14
+ let splits = [];
15
+ let lastValidTime = 0;
16
+ let lastValidIdx = -1;
17
+
18
+ for (let idx = 0; idx < course.length - 1; idx++) {
19
+ let from = course[idx];
20
+ let to = course[idx + 1];
21
+ let split = getSplit(runner, from, to);
22
+ if (!split) {
23
+ console.log('missing split for runner ' + runner.fullName + " at index " + idx, from, to);
24
+ }
25
+ console.log('split', from + '-' + to, formatTime(lastValidTime), split.diff);
26
+
27
+ if (!split) {
28
+ console.log('split not found for runner', from, to, runner.fullName);
29
+ return;
30
+ }
31
+
32
+ if (split.split !== 's') {
33
+ time += split.diff;
34
+ lastValidTime = time;
35
+ lastValidIdx = idx;
36
+ splits.push({
37
+ ...split,
38
+ time: formatTime(time)
39
+ });
40
+ console.log('result', idx, split.code, 'time', formatTime(time));
41
+ } else {
42
+ splits.push({
43
+ ...split,
44
+ time: '-'
45
+ });
46
+ }
47
+ }
48
+ runner.splits = splits;
49
+ return runner;
50
+ });
51
+ }
52
+
53
+ function getSplit(runner, from, to) {
54
+ for (let idx = 0; idx < runner.splits.length; idx++) {
55
+ if (from === 'St') {
56
+ // assumption: all runners have same start
57
+ const split = runner.splits[0];
58
+ return {
59
+ ...split,
60
+ diff: parseTime(split.time)
61
+ };
62
+ } else if (to === 'Zi') {
63
+ throw "to Zi not supported";
64
+ }
65
+
66
+ for (let j = 1; j < runner.splits.length; j++) {
67
+ if (from === runner.splits[j - 1].code && to === runner.splits[j].code) {
68
+ const prevSplit = runner.splits[j - 1];
69
+ const split = runner.splits[j];
70
+ return {
71
+ ...split,
72
+ diff: parseTime(split.time) - parseTime(prevSplit.time)
73
+ };
74
+ }
75
+ }
10
76
  }
11
- };
77
+
78
+ console.log('busted!', from, to, runner);
79
+ return null;
80
+ }
12
81
 
13
82
  function getLegTime(runner, from, to) {
14
83
  // from: [41,38]
@@ -57,11 +126,11 @@ function getLegTime(runner, from, to) {
57
126
 
58
127
  /**
59
128
  * Reorganize a single runner to the given course. The course is specified as an array
60
- * of arrays. The elements in the inner array represent the controls that shall be
61
- * combined.
62
- *
63
- * @param {*} runner
64
- * @param {*} course
129
+ * of arrays. The elements in the inner array represent the controls that shall be
130
+ * combined.
131
+ *
132
+ * @param {*} runner
133
+ * @param {*} course
65
134
  */
66
135
  module.exports.reorganizeBbn = function(runner, course) {
67
136
  assert(runner.splits.length === course.length, 'number of splits must match', runner.splits.length, course.length);
@@ -82,4 +151,62 @@ module.exports.reorganizeBbn = function(runner, course) {
82
151
  assert(runner.splits.length === splits.length, 'number of splits must not change', runner.splits.length, splits.length, course.length);
83
152
 
84
153
  runner.splits = splits;
85
- }
154
+ };
155
+
156
+
157
+ module.exports.defineSubRanking = function(input, from, to) {
158
+ let runners;
159
+ if (typeof input.categories !== 'undefined') {
160
+ runners = input.categories.flatMap(function(category) { return category.runners; })
161
+ } else {
162
+ runners = input.runners;
163
+ }
164
+
165
+ return runners.map(function(runner) {
166
+ var fromTime, toTime;
167
+ const course = [];
168
+
169
+ if (from === 'St') {
170
+ fromTime = 0;
171
+ course.push(from);
172
+ }
173
+
174
+ if (to === 'Zi') {
175
+ toTime = parseTime(runner.time);
176
+ }
177
+
178
+ for (var i = 0; i < runner.splits.length; i++) {
179
+ var split = runner.splits[i];
180
+ if (parseTime(split.time) === null) {
181
+ continue;
182
+ }
183
+
184
+ if (split.code === from && typeof fromTime === 'undefined') {
185
+ fromTime = parseTime(split.time);
186
+ course.push(split.code);
187
+
188
+ } else if (split.code === to) {
189
+ toTime = parseTime(split.time);
190
+ course.push(split.code);
191
+ break;
192
+
193
+ } else if (typeof fromTime !== 'undefined') {
194
+ course.push(split.code);
195
+ }
196
+ }
197
+
198
+ if (typeof fromTime !== 'undefined' && typeof toTime !== 'undefined' && toTime !== null) {
199
+ const time = formatTime(toTime - fromTime);
200
+ return {
201
+ fullName: runner.fullName,
202
+ time: time,
203
+ course: course.join('-')
204
+ };
205
+ }
206
+
207
+ return {
208
+ fullName: runner.fullName
209
+ };
210
+ }).filter(runner => typeof runner.time !== 'undefined')
211
+ .sort((e1, e2) => parseTime(e1.time) - parseTime(e2.time));
212
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rasifix/orienteering-utils",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "utility functions for orienteering result analyzis",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -16,8 +16,7 @@
16
16
  "url": "https://github.com/rasifix/orienteering-util.git"
17
17
  },
18
18
  "dependencies": {
19
- "chai": "^4.2.0",
20
- "iconv-lite": "^0.4.24",
21
- "mocha": "^5.2.0"
19
+ "chai": "^4.3.6",
20
+ "mocha": "^9.2.1"
22
21
  }
23
22
  }
@@ -0,0 +1,120 @@
1
+ //Format: Rank;Name;Firstname;YearOfBirth;SexMF;FedNr;Zip;Town;Club;NationIOF;StartNr;eCardNr;RunTime;StartTime;FinishTime;CtrlCode;SplitTime; ...
2
+ //Bern By Night 2018/ 2019 - Contest #3;Aeschete 1:7'500;2019-01-18;18:00;
3
+ K;4700;70;22
4
+ 1;Rufer;Bruno;1984;M;;;Bern;OLG Blanc;SUI;;969424;31:36;65:00;;32;2:13;33;2:53;40;4:49;41;5:43;43;6:41;46;8:43;47;9:40;48;11:14;49;12:56;50;13:25;52;15:05;54;16:20;49;17:06;55;17:50;54;18:50;49;19:29;45;23:18;44;24:55;42;26:37;38;28:12;39;28:54;35;30:53;Zi;31:36
5
+ 2;Allemann;Francesco;1984;M;;;Colombier;OLG Bartli und Most;SUI;;20865;32:27;65:00;;32;2:16;33;2:57;40;5:01;41;6:03;43;7:08;46;8:58;47;10:06;48;11:30;49;13:19;55;14:11;54;15:24;49;16:14;50;16:44;52;18:33;54;19:46;49;20:31;45;24:34;44;25:32;42;27:16;38;28:59;39;29:45;35;31:35;Zi;32:27
6
+ 3;Kunz;Olga;1996;F;;;Fribourg;OLV Erdmannlistein;SUI;;570587;33:53;65:00;;32;2:24;33;2:57;40;4:59;41;5:57;43;6:57;46;8:50;47;9:53;48;11:26;49;13:22;55;14:08;54;15:21;49;16:12;50;16:43;52;18:36;54;19:44;49;20:30;45;24:26;44;25:26;42;27:13;38;28:58;39;29:43;35;33:18;Zi;33:53
7
+ 4;Kunz;Simona;1990;F;;;Colombier;OLV Piz Balü;SUI;;602306;33:58;65:00;;32;2:18;33;3:00;40;4:57;41;5:58;43;6:59;46;8:50;47;9:54;48;11:25;49;13:20;55;14:12;54;15:25;49;16:15;50;16:45;52;18:38;54;19:48;49;20:32;45;24:31;44;25:33;42;27:15;38;29:01;39;29:46;35;33:17;Zi;33:58
8
+ 5;Rohrer;Ralf;1997;M;;;Diemerswil;OLV Aare;SUI;;952073;35:56;65:00;;32;2:17;33;2:53;40;4:57;41;6:03;43;7:06;46;8:56;47;10:07;48;11:34;49;13:41;55;14:29;54;15:39;49;16:29;50;16:59;52;19:03;54;20:19;49;21:05;45;25:16;44;26:39;42;28:08;38;30:13;39;33:32;35;35:19;Zi;35:56
9
+ 6;Wüst;Ilda;2000;F;;;Aarberg;OLV Erdmannlistein;SUI;;415481;36:00;65:00;;32;2:35;33;3:13;40;5:26;41;6:25;43;7:36;46;9:18;47;10:29;48;12:07;49;14:03;50;14:43;52;17:03;54;18:28;49;19:20;55;20:42;54;22:01;49;22:51;45;27:12;44;27:52;42;29:43;38;31:40;39;33:32;35;35:23;Zi;36:00
10
+ 7;Marlovic;Francesco;1934;M;;;Lugano;OL Aare;SUI;;716594;36:05;65:00;;32;2:21;33;2:48;40;4:55;41;5:59;43;7:00;46;8:54;47;9:56;48;11:32;49;13:45;50;14:15;52;17:06;54;18:17;49;19:21;55;20:37;54;21:58;49;22:52;45;27:18;44;28:19;42;29:51;38;31:38;39;33:35;35;35:31;Zi;36:05
11
+ 8;Uhlmann;Rudolf;1968;M;;;Elm;OLV Erdmannlistein;SUI;;61966;36:22;65:00;;32;2:20;33;3:05;40;5:03;41;6:06;43;7:12;46;8:49;47;9:58;48;11:30;49;13:27;55;14:16;54;15:31;49;16:22;50;16:58;52;19:00;54;20:15;49;21:02;45;25:35;44;26:44;42;28:21;38;30:10;39;33:30;35;35:43;Zi;36:22
12
+ 9;Uhlmann;Rudolf;1996;M;;;Bern;OLV Blanc;SUI;;382426;36:26;65:00;;32;2:26;33;2:56;40;5:17;41;6:18;43;7:23;46;9:07;47;10:15;48;11:45;49;13:48;50;14:24;52;16:48;54;18:16;49;19:27;55;20:47;54;22:04;49;22:50;45;27:16;44;27:59;42;29:49;38;31:38;39;33:39;35;35:45;Zi;36:26
13
+ 10;Wüst;Markus;1955;M;;;Heimiswil;OL Aare;SUI;;470001;36:39;65:00;;32;2:38;33;3:10;40;5:25;41;6:24;43;7:44;46;9:20;47;10:31;48;12:04;49;14:01;55;14:54;54;16:15;49;17:18;50;17:58;52;20:15;54;21:39;49;22:48;45;27:56;44;28:38;42;30:48;38;32:32;39;33:32;35;35:42;Zi;36:39
14
+ 10;Berner;Daria;1946;F;;;Martigny;OLG Bernstein;SUI;;971817;36:39;65:00;;32;2:41;33;3:18;40;5:41;41;6:45;43;7:53;46;9:39;47;11:45;48;13:31;49;15:48;50;16:34;52;18:44;54;20:04;49;21:04;55;22:07;54;23:20;49;24:09;45;28:37;44;29:17;42;31:01;38;33:00;39;33:53;35;36:02;Zi;36:39
15
+ 12;Rohrer;Kathrin;1949;F;;;Lausanne;OL Blanc;SUI;;486307;36:45;65:00;;32;2:35;33;3:15;40;5:42;41;6:53;43;8:03;46;9:46;47;11:01;48;12:57;49;15:03;55;16:31;54;17:59;49;18:56;50;19:34;52;21:40;54;23:15;49;24:04;45;28:05;44;28:48;42;30:43;38;32:39;39;33:59;35;35:59;Zi;36:45
16
+ 13;Schneller;Albert;2003;M;;;Lausanne;OLV Bartli und Most;SUI;;562967;36:46;65:00;;32;2:40;33;3:12;40;5:18;41;6:22;43;7:34;46;9:15;47;10:27;48;12:06;49;14:07;50;14:52;52;17:01;54;18:29;49;19:23;55;20:45;54;22:05;49;22:53;45;27:26;44;28:07;42;29:47;38;31:36;39;33:48;35;35:54;Zi;36:46
17
+ 13;Meyer;Olav;1979;M;;;Diemerswil;OL Erdmannlistein;SUI;;975503;36:46;65:00;;32;2:32;33;3:08;40;5:17;41;6:21;43;7:37;46;9:25;47;10:40;48;12:24;49;14:59;50;15:42;52;18:21;54;19:52;49;20:45;55;21:39;54;23:02;49;24:10;45;28:28;44;29:08;42;30:53;38;32:55;39;33:50;35;36:07;Zi;36:46
18
+ 15;Gustavson;Simona;1955;F;;;Neuchatel;OLG Piz Balü;SUI;;56202;36:48;65:00;;32;2:32;33;3:06;40;5:19;41;6:23;43;7:30;46;9:13;47;10:24;48;12:05;49;14:13;55;15:12;54;16:28;49;17:26;50;18:08;52;20:16;54;21:45;49;22:38;45;27:22;44;28:06;42;29:50;38;31:45;39;34:08;35;36:09;Zi;36:48
19
+ 15;Röhrig;Pia;1985;F;;;Lausanne;OLV Erdmannlistein;SUI;;531451;36:48;65:00;;32;2:30;33;3:01;40;5:08;41;6:12;43;7:16;46;9:03;47;10:12;48;11:42;49;13:44;50;14:17;52;16:46;54;18:12;49;20:11;55;21:00;54;22:15;49;23:02;45;27:16;44;28:57;42;30:37;38;33:22;39;34:10;35;36:08;Zi;36:48
20
+ 17;Schneller;Gianni;1928;M;;;Lausanne;OLV Bartli und Most;SUI;;29333;36:50;65:00;;32;2:44;33;3:20;40;5:48;41;7:02;43;8:11;46;9:50;47;11:05;48;12:45;49;15:01;55;16:34;54;18:02;49;19:06;50;19:40;52;21:42;54;23:18;49;24:07;45;28:10;44;28:52;42;30:39;38;33:27;39;34:15;35;36:11;Zi;36:50
21
+ 18;Allemann;Erwin;1926;M;;;Lugano;OLV Piz Balü;SUI;;719395;36:51;65:00;;32;2:22;33;2:50;40;4:55;41;6:01;43;7:03;46;8:53;47;10:00;48;11:36;49;13:33;50;14:12;52;16:39;54;18:08;49;19:09;55;20:57;54;22:11;49;22:57;45;27:20;44;28:03;42;29:47;38;31:36;39;33:58;35;36:10;Zi;36:51
22
+ 19;Allemann;Mia;1970;F;;;Fribourg;OLG Aare;SUI;;488927;36:53;65:00;;32;2:36;33;3:07;40;5:14;41;6:13;43;7:18;46;9:02;47;10:10;48;11:37;49;13:30;50;14:07;52;16:38;54;18:06;49;19:02;55;20:39;54;21:55;49;22:44;45;27:07;44;27:50;42;29:40;38;31:42;39;34:20;35;36:14;Zi;36:53
23
+ 20;Allemann;Lena;1948;F;;;Heimiswil;OL Bartli und Most;SUI;;36315;37:43;65:00;;32;2:28;33;3:03;40;5:09;41;6:11;43;7:14;46;8:54;47;10:03;48;11:34;49;13:28;50;14:06;52;16:36;54;17:53;49;18:46;55;19:33;54;20:42;49;21:35;45;25:42;44;29:59;42;31:38;38;33:26;39;34:12;35;36:58;Zi;37:43
24
+ 21;Hendrikson;Thomas;1949;M;;;Goldiwil;OL Aare;SUI;;689653;39:46;65:00;;32;2:44;33;3:37;40;5:47;41;6:52;43;9:48;46;11:39;47;13:21;48;15:01;49;17:37;50;18:20;52;20:22;54;21:41;49;22:37;55;23:29;54;24:41;49;25:27;45;29:47;44;30:41;42;32:28;38;34:18;39;35:32;35;39:11;Zi;39:46
25
+ 22;Regazoni;Nico;1941;M;;;Elm;OLV Bartli und Most;SUI;;359951;39:56;65:00;;32;2:29;33;3:05;40;5:36;41;6:55;43;8:15;46;10:04;47;11:21;48;13:08;49;15:33;50;16:11;52;18:47;54;20:07;49;21:18;55;22:23;54;23:49;49;24:39;45;29:37;44;30:35;42;32:32;38;34:31;39;35:52;35;39:00;Zi;39:56
26
+ 23;Zürcher;Gianni;1942;M;;;Goldiwil;OLG Erdmannlistein;SUI;;613986;40:00;65:00;;32;2:22;33;2:51;40;4:52;41;5:53;43;6:53;46;8:45;47;9:51;48;11:23;49;13:16;50;13:53;52;16:30;54;18:22;49;19:08;55;20:41;54;21:55;49;22:39;45;27:46;44;28:22;42;29:52;38;31:35;39;33:25;35;38:21;Zi;40:00
27
+ 24;Garaio;Nico;1977;M;;;Elm;OL Erdmannlistein;SUI;;523739;40:01;65:00;;32;2:23;33;2:52;40;4:54;41;6:01;43;7:01;46;8:59;47;10:07;48;11:34;49;13:35;55;14:22;54;15:36;49;16:34;50;17:04;52;19:08;54;20:17;49;21:07;45;25:27;44;29:01;42;30:46;38;32:37;39;35:41;35;38:51;Zi;40:01
28
+ 25;Marlovic;Lara;1982;F;;;Lausanne;OLG Blanc;SUI;;14176;42:24;65:00;;32;2:36;33;3:09;40;5:28;41;6:28;43;7:38;46;9:19;47;10:31;48;12:08;49;14:14;55;15:09;54;16:24;49;17:22;50;18:04;52;25:35;54;27:35;49;28:29;45;32:40;44;34:19;42;35:50;38;37:46;39;39:36;35;41:40;Zi;42:24
29
+ 26;Garaio;Olga;1972;F;;;Goldiwil;OLV Bernstein;SUI;;509018;43:00;65:00;;32;2:37;33;3:07;40;5:11;41;6:13;43;8:56;46;14:11;47;15:37;48;17:20;49;19:28;50;20:04;52;23:04;54;24:36;49;25:29;55;26:24;54;27:42;49;28:31;45;32:39;44;34:23;42;36:12;38;38:03;39;40:07;35;42:16;Zi;43:00
30
+ 27;Maudet;Nino;1934;M;;;Neuchatel;OL Bartli und Most;SUI;;734463;44:00;65:00;;32;2:49;33;3:36;40;5:53;41;7:12;43;8:30;46;11:52;47;13:15;48;15:11;49;17:35;55;19:35;54;21:02;49;22:02;50;22:52;52;25:23;54;27:03;49;28:01;45;33:11;44;33:50;42;35:57;38;38:07;39;41:00;35;43:15;Zi;44:00
31
+ 28;Hendrikson;Martin;1995;M;;;Heimiswil;OLG Erdmannlistein;SUI;;408646;44:03;65:00;;32;2:56;33;3:42;40;6:03;41;7:18;43;8:36;46;14:12;47;16:25;48;18:36;49;20:50;55;21:45;54;23:16;49;24:21;50;25:16;52;27:57;54;29:26;49;30:23;45;34:47;44;35:49;42;37:43;38;39:48;39;40:54;35;43:17;Zi;44:03
32
+ 29;Gutmann;Franca;1976;F;;;Goldiwil;OLV Erdmannlistein;SUI;;2958;44:12;65:00;;32;2:42;33;3:23;40;5:50;41;7:00;43;8:17;46;10:01;47;11:22;48;13:11;49;15:49;50;16:38;52;19:13;54;22:20;49;23:36;55;25:26;54;26:42;49;28:19;45;32:49;44;33:38;42;35:24;38;37:31;39;40:54;35;43:23;Zi;44:12
33
+ 30;Schneller;Nico;1927;M;;;Konolfingen;OLV Aare;SUI;;430084;44:28;65:00;;32;2:58;33;3:42;40;6:10;41;7:26;43;9:06;46;11:33;47;13:17;48;15:25;49;17:39;55;19:09;54;20:45;49;21:57;50;22:53;52;25:30;54;27:36;49;28:35;45;33:31;44;34:44;42;37:21;38;40:02;39;41:29;35;43:50;Zi;44:28
34
+ 30;Steinle;Steve;2005;M;;;Orbe;OLG Bernstein;SUI;;636881;44:28;65:00;;32;3:01;33;3:47;40;6:19;41;7:37;43;9:01;46;12:04;47;13:45;48;15:51;49;18:25;50;19:24;52;21:54;54;23:43;49;24:51;55;25:49;54;27:16;49;28:13;45;32:55;44;33:47;42;35:46;38;38:08;39;41:00;35;43:53;Zi;44:28
35
+ 32;Steinle;Nico;1979;M;;;Konolfingen;OL Blanc;SUI;;773883;44:30;65:00;;32;2:37;33;3:09;40;5:39;41;6:48;43;7:59;46;9:52;47;11:10;48;13:17;49;15:38;50;16:31;52;20:00;54;21:49;49;22:52;55;23:49;54;25:24;49;26:23;45;32:20;44;33:09;42;35:22;38;37:41;39;41:02;35;43:45;Zi;44:30
36
+ 32;Marlovic;Albert;1987;M;;;Aarberg;OLG Erdmannlistein;SUI;;524136;44:30;65:00;;32;3:07;33;3:50;40;6:20;41;7:29;43;8:44;46;11:55;47;13:13;48;15:09;49;17:46;50;19:04;52;21:36;54;23:29;49;24:38;55;25:37;54;27:00;49;28:07;45;32:44;44;33:32;42;35:54;38;37:59;39;41:46;35;43:49;Zi;44:30
37
+ 34;Gustavson;Gustav;1941;M;;;Konolfingen;OL Erdmannlistein;SUI;;97278;44:31;65:00;;32;2:58;33;3:43;40;6:11;41;7:27;43;9:08;46;11:35;47;13:26;48;15:38;49;17:44;55;19:11;54;20:48;49;21:59;50;23:02;52;25:26;54;27:38;49;28:38;45;33:33;44;34:43;42;37:19;38;40:03;39;41:32;35;43:51;Zi;44:31
38
+ 35;Meyer;Simon;1966;M;;;Bern;OLG Erdmannlistein;SUI;;317469;44:34;65:00;;32;2:47;33;3:29;40;6:06;41;7:19;43;8:27;46;12:34;47;14:20;48;16:15;49;18:23;55;19:35;54;21:07;49;22:18;50;23:16;52;25:38;54;27:34;49;28:30;45;33:00;44;35:55;42;37:45;38;40:00;39;41:03;35;43:55;Zi;44:34
39
+ 36;Torres;Siri;1930;F;;;Martigny;OL Piz Balü;SUI;;820582;44:36;65:00;;32;3:01;33;3:48;40;6:15;41;7:34;43;9:04;46;11:27;47;13:24;48;15:32;49;17:51;50;19:01;52;21:43;54;23:27;49;24:40;55;25:46;54;27:26;49;28:32;45;33:18;44;34:28;42;37:23;38;39:25;39;41:14;35;43:54;Zi;44:36
40
+ 37;Hendrikson;Quentin;1946;M;;;Colombier;OLG Bartli und Most;SUI;;800863;44:40;65:00;;32;2:45;33;3:15;40;5:46;41;6:58;43;8:09;46;9:58;47;11:15;48;13:04;49;15:44;55;16:49;54;18:12;49;19:17;50;20:02;52;23:03;54;25:49;49;26:54;45;31:45;44;32:37;42;35:04;38;36:59;39;40:34;35;43:56;Zi;44:40
41
+ 37;Hübscher;David;1934;M;;;Martigny;OLG Aare;SUI;;616846;44:40;65:00;;32;2:41;33;3:22;40;5:50;41;7:02;43;8:21;46;10:13;47;11:28;48;13:31;49;15:54;50;16:42;52;19:17;54;23:48;49;24:49;55;25:55;54;27:33;49;28:27;45;34:22;44;35:10;42;37:17;38;39:15;39;40:32;35;43:48;Zi;44:40
42
+ 39;Widemar;Hanna;1948;F;;;Illiswil;OLG Bernstein;SUI;;102139;44:44;65:00;;32;2:46;33;3:26;40;6:05;41;7:17;43;8:35;46;12:27;47;14:17;48;16:12;49;18:27;55;19:30;54;21:05;49;22:07;50;23:01;52;25:37;54;27:02;49;28:02;45;33:27;44;34:18;42;36:04;38;38:05;39;41:05;35;43:59;Zi;44:44
43
+ 40;Garaio;David;2001;M;;;Lausanne;OLG Aare;SUI;;358132;44:54;65:00;;32;2:57;33;3:32;40;6:09;41;7:14;43;8:22;46;12:43;47;13:56;48;15:38;49;17:39;55;19:37;54;20:54;49;22:21;50;22:54;52;25:06;54;26:24;49;27:34;45;33:40;44;34:24;42;36:05;38;38:09;39;41:06;35;44:08;Zi;44:54
44
+ 41;Torres;Mia;1946;F;;;Konolfingen;OLV Piz Balü;SUI;;436149;44:55;65:00;;32;2:54;33;3:44;40;6:22;41;7:38;43;9:05;46;11:45;47;13:31;48;15:33;49;17:58;50;19:08;52;21:39;54;23:32;49;24:34;55;25:29;54;27:24;49;28:23;45;32:51;44;33:40;42;35:40;38;37:50;39;40:50;35;44:09;Zi;44:55
45
+ 41;Wüst;Otto;1978;M;;;Martigny;OLV Aare;SUI;;747071;44:55;65:00;;32;2:52;33;3:30;40;5:57;41;7:25;43;8:42;46;11:15;47;13:18;48;15:59;49;18:11;55;19:31;54;20:57;49;22:08;50;22:58;52;25:29;54;27:25;49;28:21;45;32:54;44;33:44;42;35:33;38;37:34;39;41:06;35;44:14;Zi;44:55
46
+ 43;Rufer;Gianni;1928;M;;;Fribourg;OL Blanc;SUI;;614308;44:57;65:00;;32;3:02;33;3:38;40;6:11;41;7:24;43;8:38;46;11:24;47;12:52;48;14:40;49;17:29;55;18:59;54;20:46;49;21:42;50;23:10;52;25:43;54;27:19;49;28:13;45;32:53;44;34:26;42;36:08;38;38:14;39;41:12;35;44:14;Zi;44:57
47
+ 44;Gutmann;Lena;2004;F;;;Orbe;OL Aare;SUI;;689472;45:15;65:00;;32;3:04;33;3:49;40;6:27;41;7:40;43;9:03;46;11:41;47;13:23;48;15:31;49;17:53;55;19:15;54;21:12;49;22:18;50;23:08;52;25:43;54;27:52;49;28:50;45;33:42;44;34:36;42;37:25;38;39:33;39;41:00;35;44:24;Zi;45:15
48
+ 45;Gutmann;Lara;1977;F;;;Fribourg;OL Bernstein;SUI;;646018;50:49;65:00;;32;3:11;33;3:52;40;7:00;41;8:26;43;9:49;46;11:55;47;13:33;48;15:45;49;19:45;50;21:03;52;23:43;54;25:49;49;26:53;55;29:57;54;31:26;49;33:00;45;38:41;44;40:14;42;43:11;38;45:50;39;47:17;35;49:54;Zi;50:49
49
+ 46;Kinzle;Jan;1978;M;;;Konolfingen;OLG Erdmannlistein;SUI;;807175;51:38;65:00;;32;3:20;33;3:59;40;7:06;41;8:40;43;10:08;46;12:33;47;14:16;48;16:25;49;19:15;50;19:58;52;23:17;54;25:20;49;26:27;55;27:19;54;29:10;49;30:09;45;38:09;44;40:22;42;43:05;38;45:47;39;48:03;35;50:46;Zi;51:38
50
+ 47;Röhrig;Lars;1955;M;;;Heimiswil;OLV Bernstein;SUI;;364765;52:55;65:00;;32;3:24;33;4:08;40;7:16;41;8:42;43;10:09;46;12:27;47;14:04;48;16:10;49;19:18;55;20:58;54;22:56;49;24:08;50;25:09;52;28:09;54;30:12;49;31:23;45;37:42;44;40:20;42;43:17;38;46:07;39;48:25;35;51:52;Zi;52:55
51
+ 48;Meyer;Simona;2004;F;;;Aarberg;OLV Erdmannlistein;SUI;;729552;53:05;65:00;;32;3:12;33;4:02;40;6:28;41;7:42;43;9:18;46;14:05;47;15:41;48;17:33;49;20:00;50;20:55;52;23:28;54;27:56;49;29:09;55;30:25;54;31:57;49;33:05;45;39:23;44;40:19;42;43:13;38;46:11;39;48:23;35;51:59;Zi;53:05
52
+ 49;Meyer;Thomas;1935;M;;;Illiswil;OL Piz Balü;SUI;;850721;53:21;65:00;;32;2:54;33;3:40;40;6:21;41;7:41;43;9:17;46;13:55;47;15:36;48;17:39;49;20:05;50;21:02;52;23:54;54;25:44;49;28:52;55;29:58;54;31:41;49;32:44;45;38:18;44;41:56;42;44:18;38;46:25;39;48:37;35;52:28;Zi;53:21
53
+ 50;Uhlmann;Simona;1998;F;;;Colombier;OLV Piz Balü;SUI;;7954;53:24;65:00;;32;3:29;33;4:10;40;6:54;41;8:15;43;9:40;46;11:49;47;13:20;48;15:24;49;17:55;55;19:17;54;21:05;49;22:12;50;23:03;52;25:40;54;27:42;49;28:34;45;35:26;44;40:17;42;43:18;38;45:56;39;48:32;35;51:49;Zi;53:24
54
+ 51;Garaio;Francesco;1992;M;;;Heimiswil;OLV Blanc;SUI;;541840;53:28;65:00;;32;3:30;33;4:14;40;7:29;41;8:57;43;10:30;46;12:51;47;14:46;48;17:13;49;20:14;50;21:15;52;24:09;54;26:08;49;27:27;55;28:57;54;31:01;49;32:30;45;38:25;44;41:33;42;44:07;38;46:42;39;48:44;35;52:33;Zi;53:28
55
+ 52;Rohrer;David;1969;M;;;Orbe;OLV Piz Balü;SUI;;76894;53:59;65:00;;32;7:36;33;8:00;40;10:43;41;12:18;43;17:07;46;19:26;47;20:41;48;23:34;49;25:51;55;26:50;54;28:07;49;28:54;50;29:55;52;32:22;54;33:46;49;34:36;45;39:10;44;43:53;42;46:31;38;48:26;39;50:26;35;53:15;Zi;53:59
56
+ 53;Uhlmann;Nico;1950;M;;;Lausanne;OLV Bartli und Most;SUI;;37397;54:01;65:00;;32;2:55;33;3:40;40;6:08;41;7:21;43;8:31;46;12:21;47;13:54;48;15:40;49;17:47;55;19:05;54;20:34;49;21:32;50;25:40;52;28:11;54;29:57;49;30:51;45;38:27;44;43:59;42;46:21;38;48:11;39;50:29;35;53:16;Zi;54:01
57
+ 54;Gustavson;Pablo;1945;M;;;Aarberg;OLG Bernstein;SUI;;429414;54:40;65:00;;32;3:05;33;4:02;40;6:24;41;7:32;43;8:46;46;11:57;47;13:29;48;15:16;49;17:52;55;19:07;54;20:36;49;22:21;50;23:04;52;25:27;54;27:53;49;28:51;45;35:26;44;44:57;42;46:38;38;48:36;39;50:54;35;53:45;Zi;54:40
58
+ 55;Meyer;Franziska;1965;F;;;Goldiwil;OLG Erdmannlistein;SUI;;728073;54:50;65:00;;32;3:00;33;3:51;40;6:48;41;8:12;43;9:43;46;12:06;47;14:12;48;16:21;49;19:10;50;20:01;52;23:08;54;25:28;49;26:32;55;27:36;54;29:18;49;30:17;45;38:59;44;44:12;42;46:37;38;48:53;39;50:53;35;53:50;Zi;54:50
59
+ 56;Schneller;Eva;1941;F;;;Colombier;OL Bernstein;SUI;;649959;54:51;65:00;;32;3:23;33;4:08;40;7:05;41;8:27;43;10:04;46;12:20;47;14:06;48;16:08;49;19:15;50;20:07;52;23:15;54;25:04;49;26:17;55;27:24;54;29:16;49;30:14;45;38:55;44;44:19;42;46:42;38;49:10;39;51:15;35;53:53;Zi;54:51
60
+ 57;Hübscher;Eva;1986;F;;;Fribourg;OL Bartli und Most;SUI;;723624;54:53;65:00;;32;2:47;33;3:28;40;6:07;41;7:23;43;8:40;46;11:20;47;13:46;48;15:42;49;18:13;55;19:17;54;20:53;49;22:03;50;22:53;52;25:34;54;27:17;49;28:11;45;32:57;44;44:21;42;46:26;38;48:35;39;50:52;35;54:06;Zi;54:53
61
+ 58;Garaio;Xenia;1982;F;;;Bern;OLV Piz Balü;SUI;;478382;55:01;65:00;;32;3:17;33;4:04;40;7:02;41;8:26;43;10:06;46;12:18;47;14:08;48;16:13;49;19:31;55;21:07;54;23:09;49;24:37;50;25:44;52;28:22;54;30:15;49;31:27;45;38:18;44;44:23;42;46:42;38;49:14;39;51:21;35;54:02;Zi;55:01
62
+ 59;Berner;Gerta;1955;F;;;Goldiwil;OLG Bartli und Most;SUI;;820123;55:09;65:00;;32;3:01;33;3:55;40;6:40;41;8:21;43;9:57;46;12:14;47;14:23;48;16:46;49;19:53;55;21:35;54;24:02;49;25:54;50;27:14;52;30:19;54;33:23;49;34:35;45;41:11;44;44:24;42;46:31;38;49:23;39;51:16;35;54:27;Zi;55:09
63
+ 60;Widemar;Lia;1987;F;;;Colombier;OL Bernstein;SUI;;175536;55:14;65:00;;32;2:55;33;6:33;40;9:02;41;10:11;43;11:56;46;13:51;47;15:28;48;17:16;49;19:30;55;21:30;54;22:46;49;23:57;50;24:41;52;28:21;54;29:59;49;30:49;45;40:07;44;44:42;42;46:35;38;48:44;39;50:49;35;54:27;Zi;55:14
64
+ 61;Rohrer;Marla;1971;F;;;Lausanne;OLG Bernstein;SUI;;117630;55:28;65:00;;32;3:09;33;3:53;40;6:43;41;8:23;43;9:55;46;12:16;47;14:22;48;16:41;49;19:49;55;21:32;54;23:58;49;26:00;50;27:16;52;30:21;54;33:20;49;34:38;45;41:15;44;44:27;42;46:33;38;49:19;39;51:13;35;54:25;Zi;55:28
65
+ 62;Huber;Gianni;1958;M;;;Orbe;OLG Erdmannlistein;SUI;;120377;55:32;65:00;;32;3:17;33;4:02;40;7:01;41;8:30;43;10:00;46;12:27;47;14:29;48;16:49;49;19:35;50;20:53;52;23:56;54;25:53;49;27:20;55;29:04;54;31:10;49;32:36;45;38:31;44;42:56;42;45:59;38;48:25;39;51:31;35;54:36;Zi;55:32
66
+ 63;Gustavson;Hans;1969;M;;;Elm;OL Bernstein;SUI;;40425;55:36;65:00;;32;3:21;33;4:07;40;7:08;41;8:34;43;10:04;46;12:31;47;14:31;48;16:53;49;19:50;50;20:54;52;23:50;54;25:58;49;27:25;55;29:11;54;31:16;49;32:42;45;38:29;44;42:53;42;45:47;38;48:38;39;51:28;35;54:34;Zi;55:36
67
+ 64;Berner;Ralf;2001;M;;;Heimiswil;OL Erdmannlistein;SUI;;542830;55:54;65:00;;32;3:33;33;4:38;40;7:26;41;8:52;43;10:20;46;12:40;47;14:36;48;17:02;49;19:58;55;23:59;54;25:34;49;27:16;50;28:52;52;32:46;54;34:42;49;35:53;45;42:28;44;44:18;42;46:45;38;49:30;39;51:33;35;54:47;Zi;55:54
68
+ 65;Zürcher;Eva;1965;F;;;Elm;OLV Piz Balü;SUI;;534775;62:47;65:00;;32;3:12;33;3:59;40;6:47;41;8:09;43;9:37;46;12:02;47;14:26;48;16:28;49;19:19;55;20:56;54;22:37;49;24:18;50;25:08;52;28:02;54;29:45;49;30:54;45;36:42;44;44:36;42;47:58;38;52:03;39;56:16;35;61:17;Zi;62:47
69
+ 66;Steinle;Lena;1952;F;;;Bern;OLG Erdmannlistein;SUI;;557898;62:51;65:00;;32;3:17;33;4:11;40;7:20;41;8:37;43;10:17;46;12:34;47;14:30;48;16:51;49;19:37;50;20:55;52;23:52;54;25:54;49;27:18;55;29:01;54;31:07;49;32:34;45;38:29;44;45:21;42;48:58;38;52:03;39;56:22;35;61:21;Zi;62:51
70
+ 67;Kinzle;Carla;1964;F;;;Heimiswil;OL Blanc;SUI;;160645;62:55;65:00;;32;3:35;33;4:21;40;7:38;41;9:05;43;10:43;46;13:21;47;15:40;48;18:03;49;21:15;55;22:58;54;25:26;49;27:17;50;28:50;52;32:31;54;34:36;49;35:55;45;42:27;44;46:13;42;49:19;38;52:26;39;56:24;35;61:24;Zi;62:55
71
+ 68;Rohrer;Cloe;1959;F;;;Neuchatel;OLV Aare;SUI;;731242;63:31;65:00;;32;3:32;33;4:19;40;7:36;41;9:01;43;10:38;46;13:17;47;15:32;48;17:49;49;20:48;50;22:03;52;26:00;54;30:04;49;31:20;55;32:38;54;34:35;49;35:47;45;42:17;44;46:18;42;49:05;38;52:18;39;56:26;35;61:23;Zi;63:31
72
+ 69;Garaio;Martin;2003;M;;;Lugano;OLV Blanc;SUI;;262458;74:43;65:00;;32;2:48;33;3:33;40;5:52;41;7:04;43;8:24;46;12:38;47;15:23;48;17:40;49;20:42;50;21:45;52;31:08;54;33:36;49;34:54;55;36:43;54;38:54;49;40:12;45;53:57;44;56:12;42;61:07;38;65:01;39;68:45;35;73:46;Zi;74:43
73
+ 70;Marlovic;David;1945;M;;;Diemerswil;OLG Aare;SUI;;77829;80:13;65:00;;32;3:49;33;4:41;40;7:45;41;9:32;43;12:13;46;15:22;47;18:11;48;20:48;49;23:56;50;24:55;52;31:23;54;33:40;49;35:08;55;37:03;54;38:58;49;40:39;45;47:41;44;56:36;42;61:02;38;67:07;39;73:09;35;79:01;Zi;80:13
74
+ ;Berner;Pablo;1991;M;;;Colombier;OLG Blanc;SUI;;103947;Po.fal.;65:00;;32;3:15;33;3:57;40;6:57;41;8:18;43;9:45;46;11:54;47;13:27;48;15:37;49;18:16;55;19:20;54;21:01;49;-;50;-;52;-;54;-;49;-;45;32:08;44;33:04;42;35:32;38;37:58;39;41:11;35;44:02;Zi;Po.fal.
75
+ ;Gutmann;Olga;1973;F;;;Lausanne;OLV Bartli und Most;SUI;;356273;P.fehl.;65:00;;32;3:26;33;4:12;40;7:23;41;8:53;43;10:33;46;13:09;47;15:16;48;18:11;49;21:43;50;22:49;52;26:03;54;29:49;49;31:18;55;32:33;54;34:32;49;35:41;45;42:10;44;47:31;42;-;38;53:56;39;56:17;35;61:22;Zi;P.fehl.
76
+ ;Hübscher;Marco;1972;M;;;Goldiwil;OL Aare;SUI;;950116;P.fehl.;65:00;;32;3:25;33;4:12;40;7:14;41;8:58;43;10:35;46;13:06;47;15:13;48;17:57;49;21:51;50;22:51;52;26:18;54;30:07;49;31:22;55;32:34;54;34:22;49;35:42;45;42:14;44;47:44;42;-;38;55:01;39;56:11;35;60:48;Zi;P.fehl.
77
+ ;Rufer;Elin;1981;F;;;Heimiswil;OLV Aare;SUI;;797969;P.fehl.;65:00;;32;2:36;33;3:17;40;5:40;41;6:43;43;7:50;46;11:13;47;12:48;48;-;49;17:26;50;18:13;52;20:26;54;21:58;49;22:51;55;23:43;54;25:09;49;26:07;45;31:39;44;32:36;42;35:01;38;37:00;39;37:56;35;40:12;Zi;P.fehl.
78
+ ;Widemar;Urs;1926;M;;;Diemerswil;OLG Bartli und Most;SUI;;610058;Po.fal.;65:00;;32;3:23;33;9:04;40;14:20;41;19:48;43;-;46;-;47;-;48;49:32;49;-;55;-;54;-;49;-;50;-;52;-;54;-;49;-;45;-;44;-;42;-;38;70:14;39;71:36;35;74:21;Zi;Po.fal.
79
+ ;Berner;Ralf;1933;M;;;Diemerswil;OLV Blanc;SUI;;762624;P.fehl.;65:00;;32;4:07;33;5:04;40;9:25;41;12:12;43;17:54;46;22:14;47;24:09;48;28:17;49;31:38;55;33:25;54;35:52;49;37:44;50;39:11;52;43:18;54;52:44;49;54:25;45;62:24;44;69:42;42;74:24;38;78:14;39;-;35;-;Zi;P.fehl.
80
+ ;Steinle;Martin;2001;M;;;Lugano;OLV Erdmannlistein;SUI;;33620;Po.fal.;65:00;;32;2:25;33;2:53;40;4:55;41;5:56;43;6:58;46;8:55;47;10:05;48;11:39;49;13:42;50;14:14;52;16:35;54;18:03;49;20:05;55;-;54;-;49;-;45;24:41;44;25:36;42;27:16;38;29:04;39;29:49;35;31:46;Zi;Po.fal.
81
+ ;Zürcher;Marco;1989;M;;;Aarberg;OL Aare;SUI;;74765;P.fehl.;65:00;;32;2:26;33;3:02;40;5:10;41;6:19;43;7:29;46;9:12;47;10:18;48;-;49;14:10;50;14:47;52;17:03;54;18:25;49;19:24;55;20:26;54;21:57;49;22:47;45;27:14;44;29:01;42;30:51;38;32:41;39;34:10;35;36:04;Zi;P.fehl.
82
+ L;7100;120;31
83
+ 1;Berner;Pablo;1936;M;;;Neuchatel;OLV Aare;SUI;;730257;37:47;65:00;;31;1:53;33;2:59;34;5:24;35;6:15;36;6:39;39;8:38;34;9:42;37;10:50;38;11:51;39;12:26;40;13:59;41;14:40;44;16:47;46;17:56;47;18:42;48;19:48;49;21:09;50;21:34;52;23:05;54;23:57;55;24:44;49;25:17;53;26:47;51;27:47;54;29:15;49;29:47;45;32:24;43;33:35;42;34:35;38;35:47;36;37:10;Zi;37:47
84
+ 2;Zürcher;Rudolf;1970;M;;;Diemerswil;OL Erdmannlistein;SUI;;910414;39:21;65:00;;31;1:55;33;2:49;34;5:33;37;6:53;38;7:55;39;8:40;34;9:44;35;10:34;36;10:57;39;12:41;40;14:17;41;15:00;44;16:21;46;17:42;47;18:32;48;19:40;49;21:04;50;21:27;52;22:50;54;23:56;55;24:42;49;25:16;53;26:57;51;27:59;54;29:27;49;29:59;45;32:41;43;33:58;42;35:36;38;37:03;36;38:36;Zi;39:21
85
+ 3;Huber;Ian;2005;M;;;Bern;OLG Bartli und Most;SUI;;720658;40:09;65:00;;31;1:56;33;2:59;34;5:31;35;6:19;36;6:39;39;9:05;34;10:38;37;12:33;38;13:35;39;14:10;40;15:46;41;16:35;44;17:56;46;19:18;47;20:06;48;21:17;49;22:50;50;23:15;52;24:44;54;25:47;55;26:33;49;27:07;53;28:49;51;30:01;54;31:29;49;32:04;45;34:53;43;36:07;42;37:08;38;38:22;36;39:37;Zi;40:09
86
+ 4;Torres;David;1981;M;;;Bern;OL Piz Balü;SUI;;183643;40:10;65:00;;31;1:56;33;2:57;34;5:28;37;6:40;38;7:45;39;8:36;34;9:16;35;10:08;36;10:31;39;12:02;40;14:00;41;14:44;44;16:24;46;17:39;47;18:34;48;19:43;49;21:08;53;23:06;51;24:12;54;25:49;49;26:52;50;27:18;52;28:39;54;29:36;55;30:19;49;30:53;45;34:02;43;35:32;42;37:08;38;38:21;36;39:39;Zi;40:10
87
+ 5;Röhrig;Urs;1956;M;;;Martigny;OLV Piz Balü;SUI;;11780;40:14;65:00;;31;1:54;33;2:55;34;5:29;37;6:44;38;7:49;39;8:34;34;9:14;35;10:13;36;10:34;39;12:10;40;14:03;41;14:45;44;16:23;46;17:44;47;18:35;48;19:42;49;21:07;50;21:37;52;23:11;54;24:06;55;24:52;49;25:24;53;26:53;51;27:53;54;29:23;49;29:56;45;33:16;43;36:06;42;37:12;38;38:23;36;39:40;Zi;40:14
88
+ 6;Huber;Dirk;1968;M;;;Martigny;OL Piz Balü;SUI;;206169;40:22;65:00;;31;2:06;33;3:07;34;5:34;35;6:21;36;6:43;39;8:40;34;9:18;37;10:22;38;11:18;39;11:59;40;13:58;41;14:39;44;16:38;46;17:47;47;18:39;48;19:44;49;21:10;50;21:36;52;23:10;54;24:02;55;24:48;49;25:23;53;26:51;51;27:51;54;29:21;49;29:53;45;34:06;43;35:27;42;37:04;38;38:20;36;39:43;Zi;40:22
89
+ 7;Gustavson;Francesco;1948;M;;;Illiswil;OLG Erdmannlistein;SUI;;77360;41:11;65:00;;31;2:01;33;3:04;34;5:37;37;8:20;38;9:35;39;10:13;34;10:58;35;12:12;36;12:34;39;14:12;40;15:45;41;16:37;44;18:02;46;19:21;47;20:09;48;21:21;49;22:46;50;23:13;52;24:40;54;25:52;55;26:40;49;27:13;53;28:53;51;30:01;54;31:31;49;32:07;45;34:58;43;36:11;42;37:16;38;38:31;36;40:28;Zi;41:11
90
+ 8;Uhlmann;Olav;1975;M;;;Elm;OLV Piz Balü;SUI;;519854;41:55;65:00;;31;2:04;33;3:11;34;5:26;35;6:18;36;6:40;39;9:11;34;10:46;37;12:06;38;13:18;39;14:12;40;15:47;41;16:38;44;18:02;46;19:26;47;20:15;48;21:30;49;23:03;53;24:56;51;26:17;54;27:53;49;28:36;50;29:01;52;30:32;54;31:31;55;32:18;49;32:58;45;36:02;43;37:17;42;38:15;38;39:40;36;41:15;Zi;41:55
91
+ 9;Kunz;Albert;1947;M;;;Lausanne;OL Bartli und Most;SUI;;5488;42:09;65:00;;31;2:02;33;3:02;34;5:33;37;6:38;38;7:56;39;8:42;34;9:23;35;10:18;36;10:40;39;12:17;40;13:57;41;14:43;44;16:22;46;17:45;47;18:37;48;19:46;49;21:11;50;21:38;52;23:12;54;24:09;55;24:57;49;25:29;53;26:59;51;28:03;54;29:45;49;30:26;45;34:07;43;35:29;42;37:09;38;38:26;36;41:21;Zi;42:09
92
+ 10;Gutmann;Urs;1977;M;;;Colombier;OL Aare;SUI;;454194;42:45;65:00;;31;1:49;33;2:50;34;5:29;35;6:27;36;6:46;39;8:26;34;9:43;37;10:52;38;11:53;39;12:28;40;14:08;41;14:51;44;16:19;46;17:46;47;18:40;48;19:49;49;21:18;53;23:01;51;24:33;54;26:25;49;28:16;50;29:07;52;30:30;54;31:25;55;32:13;49;32:55;45;35:59;43;37:13;42;38:48;38;40:14;36;41:57;Zi;42:45
93
+ 11;Meyer;Simon;1940;M;;;Fribourg;OLV Piz Balü;SUI;;159871;43:17;65:00;;31;1:54;33;3:00;34;5:26;35;6:17;36;6:39;39;9:27;34;10:45;37;12:37;38;13:41;39;14:16;40;15:45;41;16:36;44;17:59;46;19:19;47;20:11;48;21:23;49;23:01;53;25:01;51;26:18;54;27:56;49;28:33;50;28:59;52;30:30;54;31:26;55;32:14;49;32:55;45;35:54;43;37:10;42;38:09;38;39:34;36;42:38;Zi;43:17
94
+ 12;Allemann;Rudolf;2007;M;;;Aarberg;OL Bartli und Most;SUI;;311617;43:26;65:00;;31;2:15;33;3:23;34;5:31;37;6:46;38;7:53;39;8:44;34;9:29;35;10:19;36;10:42;39;12:49;40;14:39;41;15:36;44;17:13;46;18:42;47;19:40;48;20:59;49;22:41;50;23:08;52;24:49;54;26:00;55;26:48;49;27:24;53;29:14;51;30:30;54;33:05;49;33:42;45;36:53;43;38:13;42;39:25;38;40:55;36;42:42;Zi;43:26
95
+ 13;Berner;Chris;1953;M;;;Lugano;OLV Aare;SUI;;680045;44:57;65:00;;31;2:00;33;3:03;34;5:30;37;6:48;38;7:56;39;8:45;34;9:28;35;10:22;36;10:45;39;12:55;40;14:39;41;15:33;44;17:08;46;18:33;47;19:26;48;20:35;49;22:39;53;26:48;51;27:56;54;29:31;49;30:15;50;30:45;52;32:14;54;33:16;55;34:29;49;34:59;45;38:02;43;39:27;42;41:21;38;42:46;36;44:17;Zi;44:57
96
+ 14;Marlovic;Berta;2007;F;;;Illiswil;OL Aare;SUI;;955461;44:58;65:00;;31;2:10;33;3:22;34;5:26;35;6:20;36;6:41;39;8:32;34;9:18;37;10:25;38;11:28;39;12:12;40;14:20;41;15:21;44;17:00;46;18:31;47;19:32;48;20:53;49;22:38;50;23:06;52;24:46;54;25:50;55;26:42;49;27:19;53;29:15;51;30:30;54;33:10;49;33:55;45;37:21;43;38:56;42;41:20;38;42:50;36;44:18;Zi;44:58
97
+ 15;Röhrig;Chris;1952;M;;;Aarberg;OLG Piz Balü;SUI;;265869;45:09;65:00;;31;2:12;33;3:23;34;5:28;35;6:25;36;6:51;39;9:47;34;10:44;37;12:10;38;13:19;39;14:03;40;15:50;41;16:44;44;18:34;46;20:05;47;20:57;48;22:18;49;24:10;53;26:47;51;27:52;54;29:35;49;30:16;50;30:48;52;32:18;54;33:18;55;34:11;49;34:49;45;38:05;43;39:29;42;41:18;38;42:48;36;44:28;Zi;45:09
98
+ 16;Röhrig;Fritz;1948;M;;;Fribourg;OLV Erdmannlistein;SUI;;684377;45:13;65:00;;31;2:06;33;3:13;34;5:29;35;6:22;36;6:48;39;9:38;34;10:48;37;12:04;38;13:22;39;14:06;40;15:52;41;16:46;44;18:13;46;19:44;47;20:42;48;22:00;49;23:57;50;24:30;52;26:19;54;27:22;55;28:13;49;28:54;53;30:56;51;32:16;54;33:58;49;34:37;45;37:58;43;39:26;42;41:23;38;42:49;36;44:29;Zi;45:13
99
+ 17;Zürcher;Franziska;1993;F;;;Neuchatel;OLG Piz Balü;SUI;;356282;45:20;65:00;;31;2:20;33;3:32;34;5:42;35;6:39;36;7:05;39;9:26;34;10:42;37;12:08;38;13:19;39;14:00;40;15:45;41;16:40;44;18:09;46;19:40;47;20:37;48;21:58;49;23:52;50;24:28;52;26:15;54;27:21;55;28:11;49;28:53;53;30:54;51;32:13;54;33:56;49;34:35;45;37:57;43;39:25;42;41:27;38;43:01;36;44:33;Zi;45:20
100
+ 18;Regazoni;Steve;1952;M;;;Neuchatel;OLV Bartli und Most;SUI;;179871;45:38;65:00;;31;2:03;33;3:10;34;5:36;37;6:51;38;8:00;39;8:50;34;9:36;35;10:46;36;11:14;39;13:14;40;15:22;41;16:32;44;18:17;46;19:47;47;20:44;48;22:03;49;23:53;53;26:06;51;27:24;54;29:57;49;30:41;50;31:04;52;33:06;54;34:03;55;34:55;49;35:34;45;39:07;43;40:38;42;41:48;38;43:19;36;44:55;Zi;45:38
101
+ 19;Allemann;Erwin;2007;M;;;Neuchatel;OLG Bartli und Most;SUI;;390941;45:45;65:00;;31;2:09;33;3:17;34;5:40;35;6:33;36;6:57;39;8:38;34;9:26;37;11:36;38;12:48;39;13:34;40;15:42;41;16:39;44;18:11;46;19:43;47;20:43;48;22:05;49;23:55;53;26:09;51;27:46;54;29:44;49;30:30;50;31:09;52;32:50;54;34:02;55;34:57;49;35:31;45;39:14;43;40:44;42;41:52;38;43:26;36;45:02;Zi;45:45
102
+ 20;Schneller;Gianni;1947;M;;;Elm;OLG Blanc;SUI;;402073;50:07;65:00;;31;1:58;33;3:00;34;5:31;35;6:55;36;7:18;39;9:20;34;10:43;37;12:04;38;13:19;39;14:52;40;16:55;41;17:50;44;19:27;46;22:19;47;23:16;48;24:31;49;26:20;50;26:50;52;28:30;54;29:43;55;30:39;49;31:25;53;33:16;51;34:47;54;38:43;49;39:39;45;43:44;43;45:10;42;46:18;38;47:43;36;49:30;Zi;50:07
103
+ 21;Hendrikson;Jan;1997;M;;;Diemerswil;OL Bartli und Most;SUI;;596055;50:15;65:00;;31;2:11;33;3:25;34;5:36;37;7:41;38;8:51;39;9:34;34;10:47;35;12:17;36;12:41;39;14:45;40;16:47;41;17:50;44;20:44;46;22:27;47;23:26;48;24:45;49;26:34;53;28:56;51;30:11;54;33:10;49;33:48;50;34:28;52;36:50;54;38:48;55;39:42;49;40:20;45;43:48;43;45:18;42;46:26;38;47:58;36;49:32;Zi;50:15
104
+ 22;Maudet;Dirk;1927;M;;;Orbe;OLV Blanc;SUI;;242539;50:43;65:00;;31;2:25;33;3:36;34;5:57;37;7:20;38;8:33;39;9:20;34;10:50;35;11:59;36;12:30;39;14:28;40;16:43;41;17:59;44;20:47;46;22:30;47;23:33;48;25:05;49;27:02;53;29:06;51;30:27;54;33:03;49;33:46;50;34:25;52;36:53;54;37:58;55;39:14;49;40:13;45;43:55;43;45:30;42;46:43;38;48:22;36;50:04;Zi;50:43
105
+ 23;Rufer;Dirk;1953;M;;;Lugano;OLG Bernstein;SUI;;931878;50:47;65:00;;31;2:16;33;3:35;34;5:53;35;7:09;36;7:45;39;9:53;34;11:09;37;12:50;38;14:32;39;15:53;40;17:51;41;18:49;44;21:06;46;22:40;47;23:43;48;25:11;49;27:00;50;27:35;52;30:04;54;31:17;55;32:18;49;33:06;53;35:24;51;36:56;54;38:59;49;39:58;45;43:56;43;45:28;42;46:41;38;48:20;36;50:13;Zi;50:47
106
+ 23;Rufer;Lars;1985;M;;;Bern;OLV Blanc;SUI;;225694;50:47;65:00;;31;2:08;33;3:17;34;5:27;37;6:49;38;8:03;39;8:56;34;10:55;35;12:26;36;13:01;39;15:06;40;17:02;41;18:01;44;20:34;46;22:28;47;23:27;48;24:52;49;26:49;50;27:22;52;29:52;54;31:00;55;32:00;49;32:53;53;35:10;51;36:49;54;39:05;49;40:01;45;44:11;43;45:49;42;46:56;38;48:26;36;50:13;Zi;50:47
107
+ 25;Rufer;Simon;2003;M;;;Lugano;OLV Bartli und Most;SUI;;281418;51:25;65:00;;31;2:11;33;3:38;34;5:55;37;7:18;38;8:32;39;9:16;34;12:16;35;13:10;36;13:55;39;15:43;40;17:40;41;18:37;44;20:59;46;22:35;47;23:35;48;25:02;49;26:58;53;29:16;51;30:36;54;33:23;49;34:14;50;34:45;52;36:38;54;38:10;55;39:27;49;40:11;45;43:53;43;45:27;42;46:34;38;48:14;36;50:37;Zi;51:25
108
+ 26;Regazoni;Lars;1939;M;;;Heimiswil;OL Blanc;SUI;;801707;51:32;65:00;;31;2:17;33;3:30;34;5:50;37;7:26;38;8:38;39;9:21;34;10:58;35;12:10;36;12:39;39;14:41;40;16:50;41;17:54;44;20:42;46;22:27;47;23:28;48;24:55;49;26:49;50;27:25;52;29:48;54;31:08;55;32:07;49;32:58;53;35:19;51;36:54;54;38:58;49;40:03;45;44:06;43;45:44;42;46:58;38;48:44;36;50:43;Zi;51:32
109
+ 27;Regazoni;Kathrin;2007;F;;;Fribourg;OLV Bernstein;SUI;;202352;53:51;65:00;;31;3:47;33;5:33;34;8:30;37;10:11;38;11:25;39;12:10;34;13:07;35;14:09;36;14:39;39;16:33;40;18:32;41;19:33;44;21:09;46;22:52;47;23:50;48;25:17;49;27:16;50;27:53;52;29:45;54;31:05;55;32:04;49;32:52;53;35:28;51;36:52;54;38:56;49;39:48;45;45:51;43;47:32;42;49:10;38;50:59;36;53:02;Zi;53:51
110
+ 28;Hendrikson;Otto;1993;M;;;Elm;OLG Erdmannlistein;SUI;;652655;59:07;65:00;;31;2:19;33;3:56;34;6:40;37;8:29;38;10:10;39;11:07;34;12:35;35;13:44;36;14:30;39;16:29;40;18:37;41;19:40;44;21:44;46;23:42;47;26:09;48;27:58;49;29:56;53;33:22;51;34:52;54;38:53;49;39:51;50;40:38;52;43:05;54;44:15;55;45:31;49;46:49;45;50:41;43;52:31;42;54:37;38;56:33;36;58:31;Zi;59:07
111
+ 29;Hübscher;Steve;1956;M;;;Orbe;OLV Bartli und Most;SUI;;903533;59:12;65:00;;31;2:40;33;4:04;34;7:24;37;9:42;38;11:10;39;12:03;34;13:08;35;14:12;36;14:41;39;16:42;40;18:47;41;19:46;44;21:41;46;23:34;47;26:20;48;28:06;49;30:05;53;33:06;51;34:55;54;38:12;49;40:04;50;40:44;52;42:59;54;44:12;55;45:31;49;47:01;45;51:07;43;52:43;42;54:30;38;56:35;36;58:31;Zi;59:12
112
+ 30;Gustavson;Erwin;2008;M;;;Martigny;OLV Erdmannlistein;SUI;;39684;65:37;65:00;;31;3:42;33;5:03;34;7:44;37;9:35;38;10:54;39;11:39;34;13:24;35;14:58;36;16:38;39;20:10;40;22:18;41;23:12;44;28:12;46;31:19;47;32:36;48;34:24;49;36:25;50;37:09;52;39:12;54;40:22;55;41:19;49;42:00;53;44:01;51;48:11;54;52:26;49;53:11;45;56:49;43;58:24;42;61:03;38;62:44;36;64:42;Zi;65:37
113
+ 31;Uhlmann;Markus;1985;M;;;Orbe;OLV Aare;SUI;;179373;66:32;65:00;;31;2:29;33;3:52;34;6:36;37;8:14;38;10:05;39;11:00;34;12:18;35;13:28;36;14:04;39;16:18;40;18:44;41;19:55;44;22:22;46;28:46;47;30:30;48;32:17;49;34:42;50;35:27;52;38:33;54;39:59;55;41:20;49;42:08;53;44:25;51;48:15;54;52:32;49;53:16;45;57:09;43;58:55;42;61:19;38;63:18;36;65:36;Zi;66:32
114
+ 32;Regazoni;Jan;1950;M;;;Fribourg;OL Erdmannlistein;SUI;;684404;98:27;65:00;;31;2:42;33;4:58;34;7:57;37;10:38;38;12:19;39;15:39;34;16:59;35;18:44;36;19:28;39;25:12;40;27:49;41;29:14;44;32:22;46;42:12;47;43:47;48;45:57;49;48:40;50;49:55;52;61:41;54;64:23;55;67:05;49;68:11;53;71:27;51;73:45;54;77:55;49;78:49;45;84:47;43;87:29;42;92:20;38;94:58;36;97:28;Zi;98:27
115
+ 33;Garaio;Tim;1967;M;;;Neuchatel;OLG Erdmannlistein;SUI;;983394;99:00;65:00;;31;2:49;33;4:52;34;7:42;35;10:39;36;11:24;39;14:15;34;16:49;37;18:58;38;22:25;39;25:20;40;28:16;41;29:35;44;34:59;46;42:15;47;44:15;48;46:30;49;49:00;53;52:59;51;57:30;54;63:54;49;65:39;50;66:41;52;70:24;54;72:33;55;74:11;49;75:31;45;85:35;43;88:02;42;92:19;38;95:15;36;97:51;Zi;99:00
116
+ 34;Kinzle;Steve;1983;M;;;Orbe;OLG Bernstein;SUI;;866947;122:01;65:00;;31;2:52;33;5:00;34;8:14;35;11:06;36;12:16;39;15:10;34;16:07;37;19:05;38;22:27;39;24:47;40;28:17;41;29:43;44;34:03;46;42:17;47;43:54;48;46:04;49;64:36;53;69:22;51;73:44;54;77:46;49;78:44;50;79:43;52;87:14;54;89:23;55;91:01;49;91:59;45;103:22;43;110:19;42;113:54;38;116:21;36;121:11;Zi;122:01
117
+ ;Rufer;Albert;1937;M;;;Aarberg;OLV Piz Balü;SUI;;597648;P.fehl.;65:00;;31;1:57;33;2:58;34;5:35;35;6:24;36;6:46;39;8:43;34;9:21;37;10:26;38;11:22;39;12:00;40;14:00;41;14:47;44;16:51;46;18:08;47;-;48;20:16;49;22:34;53;26:45;51;27:52;54;30:08;49;30:45;50;31:08;52;32:39;54;33:42;55;-;49;34:14;45;38:16;43;39:37;42;41:29;38;42:53;36;-;Zi;P.fehl.
118
+ ;Regazoni;Gianni;1961;M;;;Heimiswil;OLG Piz Balü;SUI;;729295;P.fehl.;65:00;;31;2:01;33;3:12;34;5:37;37;6:54;38;8:07;39;8:52;34;10:54;35;12:26;36;13:03;39;15:13;40;18:38;41;19:42;44;21:45;46;23:36;47;26:16;48;28:02;49;29:55;53;33:18;51;34:59;54;38:51;49;39:55;50;40:46;52;42:56;54;44:20;55;45:40;49;46:56;45;53:40;43;56:18;42;61:10;38;63:30;36;-;Zi;P.fehl.
119
+ ;Torres;Gerta;1944;F;;;Aarberg;OL Aare;SUI;;237075;Po.fal.;65:00;;31;2:14;33;3:31;34;5:30;37;-;38;10:06;39;10:58;34;12:09;35;13:22;36;13:51;39;15:46;40;17:38;41;18:27;44;20:58;46;22:23;47;23:19;48;24:32;49;26:11;53;29:01;51;30:16;54;33:07;49;33:45;50;34:25;52;36:46;54;38:40;55;39:59;49;40:42;45;43:47;43;45:15;42;46:19;38;47:46;36;49:22;Zi;Po.fal.
120
+ ;Rohrer;Markus;2004;M;;;Aarberg;OLV Aare;SUI;;20007;Po.fal.;65:00;;31;2:05;33;3:14;34;5:28;37;7:33;38;8:39;39;9:18;34;10:51;35;12:07;36;12:30;39;14:32;40;17:30;41;18:25;44;20:46;46;22:18;47;23:12;48;24:29;49;26:10;53;29:02;51;30:14;54;33:09;49;33:46;50;-;52;-;54;-;55;-;49;-;45;43:42;43;45:38;42;46:29;38;47:53;36;49:17;Zi;Po.fal.
@@ -0,0 +1,102 @@
1
+ var { parse } = require('../lib/oware');
2
+ var { RankingBuilder } = require('../lib/ranking');
3
+ var { readFileSync } = require('fs');
4
+ var should = require('chai').should();
5
+
6
+ require.extensions['.csv'] = function (module, filename) {
7
+ module.exports = readFileSync(filename, 'cp1252');
8
+ };
9
+
10
+ var words = readFileSync('./test/butterfly-oware.csv', 'utf-8');
11
+ var assert = require('assert');
12
+
13
+ describe('builder', function() {
14
+ var event;
15
+
16
+ before(function() {
17
+ event = parse(words);
18
+ });
19
+
20
+ describe('#parse()', function() {
21
+
22
+ it('should return parsed event', function() {
23
+ event.should.be.an('object');
24
+ });
25
+
26
+ it('should have two categories', function() {
27
+ console.log(event.categories.length);
28
+ event.categories.length.should.equal(2);
29
+ });
30
+
31
+ describe('parsed ranking', function() {
32
+ describe('category L', function() {
33
+ var builder;
34
+
35
+ before(function() {
36
+ let category = event.categories.find(category => category.name === 'L');
37
+ builder = new RankingBuilder(category.runners);
38
+ });
39
+
40
+ it('should have category L with 4 courses', function() {
41
+ builder.courses.should.be.an('array');
42
+ builder.courses.length.should.equal(4);
43
+ });
44
+
45
+ it('should have course 0', function() {
46
+ builder.courses[0].code.should.equal('St,31,33,34,35,36,39,34,37,38,39,40,41,44,46,47,48,49,50,52,54,55,49,53,51,54,49,45,43,42,38,36,Zi');
47
+ builder.courses[0].runners.length.should.equal(8);
48
+ builder.courses[0].runners.should.eql([79, 81, 84, 92, 94, 95, 98, 101]);
49
+ });
50
+
51
+ it('should have course 1', function() {
52
+ builder.courses[1].code.should.equal('St,31,33,34,37,38,39,34,35,36,39,40,41,44,46,47,48,49,50,52,54,55,49,53,51,54,49,45,43,42,38,36,Zi');
53
+ builder.courses[1].runners.length.should.equal(11);
54
+ builder.courses[1].runners.should.eql([80, 83, 85, 87, 90, 102, 104, 105, 108, 109, 110]);
55
+ });
56
+
57
+ it('should have course 2', function() {
58
+ builder.courses[2].code.should.equal('St,31,33,34,37,38,39,34,35,36,39,40,41,44,46,47,48,49,53,51,54,49,50,52,54,55,49,45,43,42,38,36,Zi');
59
+ builder.courses[2].runners.length.should.equal(11);
60
+ builder.courses[2].runners.should.eql([82, 91, 96, 99, 100, 103, 106, 107, 114, 115, 116]);
61
+ });
62
+
63
+ it('should have course 3', function() {
64
+ builder.courses[3].code.should.equal('St,31,33,34,35,36,39,34,37,38,39,40,41,44,46,47,48,49,53,51,54,49,50,52,54,55,49,45,43,42,38,36,Zi');
65
+ builder.courses[3].runners.length.should.equal(8);
66
+ builder.courses[3].runners.should.eql([86, 88, 89, 93, 97, 111, 112, 113]);
67
+ });
68
+
69
+ it('should produce ranking', function() {
70
+ let ranking = builder.getRanking();
71
+ });
72
+ });
73
+
74
+ describe('category K', function() {
75
+ var builder;
76
+
77
+ before(function() {
78
+ let category = event.categories.find(category => category.name === 'K');
79
+ builder = new RankingBuilder(category.runners);
80
+ });
81
+
82
+ it('should have category K with 2 courses', function() {
83
+ builder.courses.length.should.equal(2);
84
+ });
85
+
86
+ it('should have course 0', function() {
87
+ builder.courses[0].code.should.equal('St,32,33,40,41,43,46,47,48,49,50,52,54,49,55,54,49,45,44,42,38,39,35,Zi');
88
+ builder.courses[0].runners.length.should.equal(40);
89
+ builder.courses[0].runners.should.eql([1, 6, 7, 9, 11, 13, 14, 16, 18, 19, 20, 21, 22, 23, 26, 29, 31, 32, 33, 36, 38, 41, 45, 46, 48, 49, 51, 55, 56, 62, 63, 66, 68, 69, 70, 72, 73, 74, 77, 78]);
90
+ });
91
+
92
+ it('should have course 1', function() {
93
+ builder.courses[1].code.should.equal('St,32,33,40,41,43,46,47,48,49,55,54,49,50,52,54,49,45,44,42,38,39,35,Zi');
94
+ builder.courses[1].runners.length.should.equal(38);
95
+ builder.courses[1].runners.should.eql([2, 3, 4, 5, 8, 10, 12, 15, 17, 24, 25, 27, 28, 30, 34, 35, 37, 39, 40, 42, 43, 44, 47, 50, 52, 53, 54, 57, 58, 59, 60, 61, 64, 65, 67, 71, 75, 76]);
96
+ });
97
+
98
+ });
99
+ });
100
+
101
+ });
102
+ });
package/test/test.js CHANGED
@@ -7,4 +7,5 @@ function importTest(name, path) {
7
7
  describe('all tests', function() {
8
8
  importTest('oware', './oware-test.js');
9
9
  importTest('solv', './solv-test.js');
10
+ importTest('ranking', './ranking-test.js');
10
11
  });