compact-contacts.js
1 /** 2 * Compact and deduplicate contacts_json objects. 3 * 4 * - Deduplicates email_addresses (by email value, case-insensitive) 5 * - Deduplicates phone_numbers (by number value after stripping whitespace) 6 * - Deduplicates social_profiles (by URL) 7 * - Removes keys with null values, empty arrays, or empty strings 8 * 9 * Reusable utility: imported by the one-off compaction script AND 10 * wired into the enrich stage so new contacts are always compact. 11 */ 12 13 /** 14 * Compact a contacts_json object: dedup arrays, strip empties/nulls. 15 * @param {Object} contacts - Parsed contacts_json object 16 * @returns {Object} Compacted contacts object (new object, original not mutated) 17 */ 18 export function compactContacts(contacts) { 19 if (!contacts || typeof contacts !== 'object' || Array.isArray(contacts)) { 20 return contacts; 21 } 22 23 const result = { ...contacts }; 24 25 // Deduplicate email_addresses by email value (case-insensitive) 26 if (Array.isArray(result.email_addresses)) { 27 const seen = new Set(); 28 result.email_addresses = result.email_addresses.filter(entry => { 29 if (!entry || typeof entry !== 'object') return false; 30 const key = (entry.email || entry.address || '').toLowerCase().trim(); 31 if (!key || seen.has(key)) return false; 32 seen.add(key); 33 return true; 34 }); 35 } 36 37 // Deduplicate phone_numbers by number value (strip all whitespace) 38 if (Array.isArray(result.phone_numbers)) { 39 const seen = new Set(); 40 result.phone_numbers = result.phone_numbers.filter(entry => { 41 if (!entry || typeof entry !== 'object') return false; 42 const key = (entry.number || '').replace(/\s+/g, ''); 43 if (!key || seen.has(key)) return false; 44 seen.add(key); 45 return true; 46 }); 47 } 48 49 // Deduplicate social_profiles by URL 50 if (Array.isArray(result.social_profiles)) { 51 const seen = new Set(); 52 result.social_profiles = result.social_profiles.filter(entry => { 53 if (!entry || typeof entry !== 'object') return false; 54 const key = (entry.url || '').trim(); 55 if (!key || seen.has(key)) return false; 56 seen.add(key); 57 return true; 58 }); 59 } 60 61 // Remove keys with null values, empty arrays, or empty strings 62 for (const key of Object.keys(result)) { 63 const val = result[key]; 64 if (val === null || val === '' || (Array.isArray(val) && val.length === 0)) { 65 delete result[key]; 66 } 67 } 68 69 return result; 70 }