This commit is contained in:
2026-04-13 11:25:40 +09:00
commit 8a24c92395
67 changed files with 276281 additions and 0 deletions

106
compare-csv-phone.ts Normal file
View File

@@ -0,0 +1,106 @@
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { parse } from "csv-parse/sync";
const FILE_A = process.env.COMPARE_FILE_A ?? path.resolve(process.cwd(), "a.csv");
const FILE_B = process.env.COMPARE_FILE_B ?? path.resolve(process.cwd(), "b.csv");
const PHONE_COL_A = process.env.COMPARE_PHONE_COL_A ?? "phone";
const PHONE_COL_B = process.env.COMPARE_PHONE_COL_B ?? "phone";
const MODE = process.env.COMPARE_MODE ?? "intersection";
const OUTPUT_FILE =
process.env.COMPARE_OUTPUT_FILE ?? path.resolve(process.cwd(), `compare-${MODE}.csv`);
type CsvRow = Record<string, string>;
function normalizePhone(value: string): string {
return value.replace(/\D+/g, "");
}
function toCsv(rows: CsvRow[]): string {
if (rows.length === 0) {
return "";
}
const headers = Array.from(
rows.reduce((set, row) => {
for (const key of Object.keys(row)) {
set.add(key);
}
return set;
}, new Set<string>()),
);
const escape = (value: string): string => {
if (/[",\n\r]/.test(value)) {
return `"${value.replace(/"/g, "\"\"")}"`;
}
return value;
};
const lines = [headers.join(",")];
for (const row of rows) {
lines.push(headers.map((header) => escape(row[header] ?? "")).join(","));
}
return `${lines.join("\n")}\n`;
}
async function readCsv(filePath: string): Promise<CsvRow[]> {
const content = await readFile(filePath, "utf8");
return parse(content, {
columns: true,
skip_empty_lines: true,
}) as CsvRow[];
}
async function main(): Promise<void> {
const [rowsA, rowsB] = await Promise.all([readCsv(FILE_A), readCsv(FILE_B)]);
const phonesB = new Set(
rowsB.map((row) => normalizePhone(row[PHONE_COL_B] ?? "")).filter(Boolean),
);
const matches: CsvRow[] = [];
const onlyA: CsvRow[] = [];
for (const row of rowsA) {
const normalized = normalizePhone(row[PHONE_COL_A] ?? "");
if (!normalized) {
continue;
}
if (phonesB.has(normalized)) {
matches.push({
source: "a",
normalized_phone: normalized,
...row,
});
} else {
onlyA.push({
source: "a",
normalized_phone: normalized,
...row,
});
}
}
let outputRows: CsvRow[];
switch (MODE) {
case "intersection":
outputRows = matches;
break;
case "only_a":
outputRows = onlyA;
break;
default:
throw new Error(`Unsupported COMPARE_MODE: ${MODE}. Use intersection or only_a.`);
}
await writeFile(OUTPUT_FILE, toCsv(outputRows), "utf8");
process.stdout.write(`${OUTPUT_FILE}\n`);
}
main().catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
process.stderr.write(`${message}\n`);
process.exitCode = 1;
});