Skip to content

Instantly share code, notes, and snippets.

@justinsbarrett
Created November 16, 2021 19:03
Show Gist options
  • Save justinsbarrett/e6ceb1dbdc1c0aefb41ea5785256b20a to your computer and use it in GitHub Desktop.
Save justinsbarrett/e6ceb1dbdc1c0aefb41ea5785256b20a to your computer and use it in GitHub Desktop.
Mark matching records between two tables in Airtable.
const settings = input.config({
title: "Mark Matching Records",
description: `Searches an incoming data table for records matching those in a \"master\" table.
Any matching records are marked via a checkbox field.`,
items: [
input.config.table("masterTable", {
label: "Master table",
description: "The table containing the master list."
}),
input.config.field("masterField", {
parentTable: "masterTable",
label: "Master field",
description: "The field in the master table that contains the data you wish to search for in the incoming data table.",
}),
input.config.view("selectedView", {
parentTable: "masterTable",
label: "Selected view",
description: `The view in the master table that will display matching records. This view should have a filter to only show
records with a check in the selected field (set below).`
}),
input.config.field("selectedField", {
parentTable: "masterTable",
label: "Selected field",
description: "A checkbox field used to mark matching records."
}),
input.config.select("clearCheck", {
label: "Remove old matches?",
description: "Remove selection from previously-matched records in the master table?",
options: [{value: "Yes"}, {value: "No"}]
}),
input.config.table("incomingTable", {
label: "Incoming table",
description: "The table containing the data to compare against the master table."
}),
input.config.field("incomingMatchField", {
parentTable: "incomingTable",
label: "Incoming match field",
description: "The field to compare against the master field in the master table."
}),
input.config.field("incomingNoteField", {
parentTable: "incomingTable",
label: "Incoming note field",
description: "A text field where notes can be added for non-matching emails."
})
]
})
/**
* Update records in a table
* @param {Table} table
* @param {object[]} updates
* @param {string} updates[].id
* @param {object} updates[].fields
* @param {boolean} showProgress
*/
async function updateRecords (table, updates, showProgress=false) {
const total = updates.length
let updateCount = 0
while (updates.length > 0) {
if (showProgress) {
updateCount += Math.min(50, updates.length)
output.clear()
output.text(`Updating records (${updateCount} of ${total})`)
}
await table.updateRecordsAsync(updates.slice(0, 50));
updates = updates.slice(50);
}
}
async function clearOldRecords() {
const matchQuery = await settings.selectedView.selectRecordsAsync({
fields: [settings.selectedField.name]
});
const updates = []
for (let record of matchQuery.records)
updates.push({
id: record.id,
fields: {
[settings.selectedField.name]: false
}
});
await updateRecords(settings.masterTable, updates)
}
async function main() {
// Clear formerly-checked records
if (settings.clearCheck === "Yes") {
output.text("Clearing old matches...")
await clearOldRecords()
}
// Build the catalog from the master list
let catalog = {};
const masterQuery = await settings.masterTable.selectRecordsAsync({
fields: [
settings.masterField.name,
settings.selectedField.name
]
});
for (let record of masterQuery.records) {
// Skip records that might have an empty field
if (!record.getCellValueAsString(settings.masterField)) {
continue
}
catalog[record.getCellValueAsString(settings.masterField).trim()] = record.id;
}
output.text(`Master list size: ${Object.keys(catalog).length} records.`);
// Parse the incoming list and build two arrays: one for marking matching records
// in the master table, the other for marking non-matching records in the incoming table
let masterUpdates = [];
let incomingUpdates = [];
const incomingQuery = await settings.incomingTable.selectRecordsAsync({
fields: [
settings.incomingMatchField.name,
settings.incomingNoteField.name
]
});
for (let record of incomingQuery.records) {
let email = record.getCellValueAsString(settings.incomingMatchField).trim();
if (catalog[email]) {
masterUpdates.push({
id: catalog[email],
fields: {
[settings.selectedField.name]: true
}
});
}
incomingUpdates.push({
id: record.id,
fields: {
[settings.incomingNoteField.name]: catalog[email] ? "Match found" : "No match found"
}
});
}
output.text(`${masterUpdates.length} matches found in ${incomingQuery.records.length} incoming records`);
output.text("--------------------------------------------");
// Update the master table records
output.text(`Updating ${masterUpdates.length} master records...`);
await updateRecords(settings.masterTable, masterUpdates)
// Update the incoming table records
output.text(`Updating ${incomingUpdates.length} incoming records...`);
await updateRecords(settings.incomingTable, incomingUpdates)
}
await main()
@justinsbarrett
Copy link
Author

This is an improved version of the script that I wrote for this forum post: https://community.airtable.com/t/re-creating-a-script-to-search-for-a-list-of-values/44218 . I was still in my early "sloppy" coding days when I first wrote the original version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment