// usage: node match.js const fs=require('fs'),path=require('path');const D=process.argv[2]; const norm=a=>(a||'').toUpperCase().replace(/[.,#]/g,' ').replace(/\s+/g,' ').trim() .replace(/ AVENUE\b/,' AVE').replace(/ STREET\b/,' ST').replace(/ WAY SOUTHWEST\b/,' WAY SW').replace(/ UNIT .*$/,''); const rows=JSON.parse(fs.readFileSync(path.join(D,'od_addr_combined.json'))); const yrs=['2023','2024','2025','2026janoct']; const calls={};rows.forEach(r=>calls[norm(r.a)]=r); const rr=JSON.parse(fs.readFileSync(path.join(D,'rrio_20plus.json'))); const B={};rr.forEach(r=>{const a=norm(r.originaladdress1);B[a]=B[a]||{a,names:[],units:0};B[a].names.push(r.propertyname);B[a].units+= +r.rentalhousingunits}); const ranked=rows.slice().sort((x,y)=>y.t-x.t);const rank={};let prev=null,rk=0;ranked.forEach((r,i)=>{if(r.t!==prev){rk=i+1;prev=r.t}rank[norm(r.a)]=rk}); const out=Object.values(B).map(b=>{const c=calls[b.a];return {...b,t:c?c.t:0,v:c?c.v:{},rank:c?rank[b.a]:null,per100:(c?c.t:0)/b.units*100}}); fs.writeFileSync(path.join(D,'rrio_with_calls.json'),JSON.stringify(out)); const big=out.filter(b=>b.units>=50);console.log('registered 50+ unit addresses',big.length,'with any OD call',big.filter(b=>b.t>0).length,'total calls at them',big.reduce((s,b)=>s+b.t,0),'units',big.reduce((s,b)=>s+b.units,0)); out.sort((x,y)=>y.t-x.t).slice(0,40).forEach(b=>console.log(b.t,'rank',b.rank,b.units+'u',(b.per100).toFixed(1)+'/100u',b.a,'|',b.names.join('; ')));