// Individuals vs organizations for the two main I-638 committees (dataset v2 analysis). // Usage: node indorg.js (rawDir holds con_HEYWL--535.json and con_FREEW--138.json from i638_campaign_finance.js) const fs = require('fs'), path = require('path'); const RAW = process.argv[2]; const r2 = x => Math.round(x * 100) / 100, r4 = x => Math.round(x * 1e4) / 1e4; const lab = { 'HEYWL--535': "Let's Go Washington (Yes; also backs I-001 and I-645)", 'FREEW--138': 'No Hate in WA State (No; also opposes I-001)' }; const rows = []; for (const f of ['HEYWL--535', 'FREEW--138']) { const c = JSON.parse(fs.readFileSync(path.join(RAW, 'con_' + f + '.json'))); const tot = c.reduce((t, x) => t + +x.amount, 0); const isSmall = x => /^(SMALL CONTRIBUTIONS|MISCELLANEOUS RECEIPTS)$/i.test(x.contributor_name); const ind = c.filter(x => x.contributor_category === 'Individual'); const by = {}; ind.filter(x => !isSmall(x)).forEach(x => { const k = x.contributor_name.trim().toUpperCase(); by[k] = (by[k] || 0) + +x.amount; }); const s = Object.entries(by).sort((a, b) => b[1] - a[1]); const indT = ind.reduce((t, x) => t + +x.amount, 0), hey = by['HEYWOOD BRIAN'] || 0; const small = ind.filter(isSmall); rows.push({ committee: lab[f], total: r2(tot), individuals_total: r2(indT), individuals_share: r4(indT / tot), individuals_excluding_heywood_share: r4((indT - hey) / tot), organizations_total: r2(tot - indT), organizations_share: r4((tot - indT) / tot), named_individual_contributors: s.length, unitemized_small_receipts: small.length, unitemized_small_total: r2(small.reduce((t, x) => t + +x.amount, 0)), largest_individual_total: r2(s[0][1]), largest_individual_other_than_heywood: r2(s.find(e => e[0] !== 'HEYWOOD BRIAN')[1]) }); } console.log(JSON.stringify(rows, null, 1));