Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 8x 8x 7x 20x 7x 7x 16x 16x 16x 56x 56x 56x 16x 7x 6x 5x 5x 10x 13x 16x 7x 5x 2x 4x 2x 4x 4x 2x | /**
* @module scripts/statskontoret/extractors/rows-to-records
* @description Convert raw 2-D sheet rows into header-keyed records.
*
* Used by every Statskontoret domain workflow (headcount, budget outturn,
* budget time-series) — the headers vary across workbooks but the parsing
* approach is identical, so it lives in its own module.
*
* @author Hack23 AB
* @license Apache-2.0
*/
import { normalizeKey } from '../internal/text.js';
export function rowsToRecords(
rows: readonly (readonly string[])[],
headerRowIndex?: number,
): Record<string, string>[] {
const resolvedHeaderIndex = headerRowIndex ?? findLikelyHeaderRow(rows);
if (resolvedHeaderIndex < 0) return [];
const headers = rows[resolvedHeaderIndex].map(
(header, index) => header.trim() || `column_${index + 1}`,
);
const records: Record<string, string>[] = [];
for (const row of rows.slice(resolvedHeaderIndex + 1)) {
const record: Record<string, string> = {};
let hasValue = false;
for (let i = 0; i < headers.length; i++) {
const value = row[i]?.trim() ?? '';
Eif (value) hasValue = true;
record[headers[i]] = value;
}
Eif (hasValue) records.push(record);
}
return records;
}
export function findLikelyHeaderRow(rows: readonly (readonly string[])[]): number {
for (let i = 0; i < rows.length; i++) {
const normalized = rows[i].map(normalizeKey);
const headcountScore = [
normalized.some((cell) => cell.includes('myndighet')),
normalized.some((cell) => cell.includes('departement')),
normalized.some((cell) => cell.includes('arsarbetskrafter') || cell === 'aa'),
normalized.some((cell) => cell === 'ar' || cell === 'year'),
].filter(Boolean).length;
if (headcountScore >= 2) return i;
const budgetScore = [
normalized.some((cell) => cell.includes('utfall') || cell.includes('outturn')),
normalized.some(
(cell) =>
cell.includes('inkomst') || cell.includes('utgift') || cell.includes('anslag'),
),
normalized.some((cell) => cell === 'ar' || cell.includes('kalenderar') || cell === 'year'),
normalized.some((cell) => cell.includes('budget') || cell.includes('belopp')),
].filter(Boolean).length;
Eif (budgetScore >= 2) return i;
}
return rows.findIndex((row) => row.filter((cell) => cell.trim()).length >= 2);
}
|