actually - its the “Flow chart” app
thanks!
yes that’s the one…
not awesome to build a family tree, but it’s a start
++
I am noticing that some of the records disappear when I Run the script. And then re-appear when I run the script again. Strange bug… not sure I understand why. Anyone come across this?
I am noticing that some of the records disappear when I Run the script. And then re-appear when I run the script again. Strange bug… not sure I understand why. Anyone come across this?
Im guessing you have a filter that is showing/hiding them based on the updates.
Im guessing you have a filter that is showing/hiding them based on the updates.
I don’t have an Airtable filter applied to that spreadsheet.
The table is setup to allow linking of multiple records… not sure if that is causing the problem.
I could try to share a screenshot if that’s helpful.
Hi, thanks a lot for this useful script.
I tried to run it in an automation but it outputs an error: “TypeError: output.clear is not a function
at main on line 114”
The same script works fine as a block script.
How can I fix this please ?
Hi, thanks a lot for this useful script.
I tried to run it in an automation but it outputs an error: “TypeError: output.clear is not a function
at main on line 114”
The same script works fine as a block script.
How can I fix this please ?
Hi, yes, the output is only available in the script. If you remove all lines that start with output.
like: output.text(…), output.clear(…). output.markdown(…), etc
Thanks. This is excellent.
Hi there and thanks for sharing @openside! Incredibly valuable solution.
My question is, if you are working in a dynamic scenario where a linked relationship could be unlinked from one end, is there a way to run a version of this script to check for this and do the reverse and remove links?
Thanks in advance!
Hi @Brett_Snelgrove - I don’t have plans to modify the script, maybe someone else can run with that idea. It does get tricky trying to keep both ways working and in sync, probably why Airtable never implemented it. Sorry can’t be of more help this time around.
Hello @openside . Thanks for this script. Very useful.
I am just using it in a scripting block. No fancy automation. When using this script, I have noticed a limitation / bug which I would like to confirm with you. It does not look like anybody else has pointed this out, so I may be mistaken here:
The script does not remove the last backlink in a cell of the destination column. Let me explain with an example. Consider three records:
ID | Predecessor ID | Successor ID
1 | | 2, 3
2 | 1 | 3
3 | 1, 2 |
If I remove “1” from the “Predecessor ID” column of record number 2, and if I then run the script, the “Successor ID” column of record 1 gets updated correctly. It ends up with just the value “3” in that column.
If I remove the “2” from the “Predecessor ID” column of record number 3, and if I then run the script, the “3” does not get removed from the “Successor ID” column of record number 2.
Looking at the code, I saw that the reason for this behaviour could be because a “destination” cell is touched ONLY if it has been mentioned somewhere in the source column of any record. If a destination cell has not been mentioned, then its value does not get updated at all.
Is this a known issue? Do you have any quick way to fix this? Would it work to just (programmatically) delete the entire column’s values at the start of the script?
I made the following change, which worked. After line 78, I added the following line:
if (!treenrec.id]) treebrec.id] = o]
But this obviously increases the running time of the script tremendously.
Manually, I know that I can just click on the column header to select the entire column and then press the delete key to clear the column instantly. What is the syntax to do this programmatically?
@Anjan - I haven’t tested this, but try the updated code: Same Table Backlinks · GitHub
I expanded on your change to check all and update if there were some remaining that need to be cleared, and ignore if it was already empty, so should reduce the time running.
Let me know if it works.
Disclaimer: I have the most rudimentary understanding of JS that it’s possible to have (but I’m trying to learn!)
Thank you for creating this script - it should do all I need to do, but I’m getting an error message as follows:
TypeError: null is not an object (evaluating ‘pRecord.getCellValue(dest).length’)
at asyncFunctionResume
at promiseReactionJob
Please can someone who understands what they’re doing help me to correct this!
Disclaimer: I have the most rudimentary understanding of JS that it’s possible to have (but I’m trying to learn!)
Thank you for creating this script - it should do all I need to do, but I’m getting an error message as follows:
TypeError: null is not an object (evaluating ‘pRecord.getCellValue(dest).length’)
at asyncFunctionResume
at promiseReactionJob
Please can someone who understands what they’re doing help me to correct this!
You got TypeScript’d. Typing aside, null is not an object is a pretty strong indication that some part of the code is executing sooner than it was intended to. Making matters even more interesting is the fact that you’re dealing with a null value; this is significant because unlike undefined objects, anything js labels as null had to have been manually set to that nil-value by someone or something. The Monaco linter hasn’t given up any line numbers? Can you try copy-pasting the code into VSC? Ideally with all of the major js+ts plugins installed, the entire software suite is free.
I keep getting an error message when I try to run this script.
TypeError: Cannot read property 'length' of null
at findChildren on line 92
at async syncLinks on line 108
at async main on line 116
I think I have everything configured correctly. Any suggestions on what to try next?
I keep getting an error message when I try to run this script.
TypeError: Cannot read property 'length' of null
at findChildren on line 92
at async syncLinks on line 108
at async main on line 116
I think I have everything configured correctly. Any suggestions on what to try next?
Took another look at that error, it seems like your loop isn’t terminating correctly, try changing line 116 to
if(foundItem>1)
otherwise, on its last check, the code goes
- foundItem.length is 0 - true
- let’s go splice our obviously empty array
- uh, oh, something went wrong, boss
Using the length instead of a direct check for the loop is a smart move, mind you, because it’s way faster than accessing the whole Object directly and it’s likely that the code will re-evaluate line 116 a bunch of times so the performance gain could be significant, depending on the base. The drawback here was a bit of a loss on the readability front but hopefully this gets it working.
Took another look at that error, it seems like your loop isn’t terminating correctly, try changing line 116 to
if(foundItem>1)
otherwise, on its last check, the code goes
- foundItem.length is 0 - true
- let’s go splice our obviously empty array
- uh, oh, something went wrong, boss
Using the length instead of a direct check for the loop is a smart move, mind you, because it’s way faster than accessing the whole Object directly and it’s likely that the code will re-evaluate line 116 a bunch of times so the performance gain could be significant, depending on the base. The drawback here was a bit of a loss on the readability front but hopefully this gets it working.
Thanks. I’ve tried changing line 116. I get the following error:
ERROR
SyntaxError: Unexpected token ‘}’
on line 1
at a on line 1
at Generator._invoke on line 1
at Generator.F.forEach.u. .as next] on line 1
at u on line 1
at o on line 1
on line 1
on line 1
Which makes me think there is a bracket out of place, but I’m not sure where that bracket is.
Thanks. I’ve tried changing line 116. I get the following error:
ERROR
SyntaxError: Unexpected token ‘}’
on line 1
at a on line 1
at Generator._invoke on line 1
at Generator.F.forEach.u. cas next] on line 1
at u on line 1
at o on line 1
on line 1
on line 1
Which makes me think there is a bracket out of place, but I’m not sure where that bracket is.
Could you share a screenshot of the change that you made to line 116, including as much of the code that follows it as possible?
Thanks. I’ve tried changing line 116. I get the following error:
ERROR
SyntaxError: Unexpected token ‘}’
on line 1
at a on line 1
at Generator._invoke on line 1
at Generator.F.forEach.u. cas next] on line 1
at u on line 1
at o on line 1
on line 1
on line 1
Which makes me think there is a bracket out of place, but I’m not sure where that bracket is.
Thanks! Here is a screenshot.
I re-added the code so line 116 is now line 120.
Let me know if there is anything else that would be helpful to share.
Thanks! Here is a screenshot.
I re-added the code so line 116 is now line 120.
Let me know if there is anything else that would be helpful to share.
I think that there may be some confusion here. There have been multiple scripts shared in this thread, and the tip from @Dominik_Bosnjak is related to line 116 in the latter of the two—the post by @Theo_Michel in September of 2020—which looks like this:
if(foundItem >= 0) {
The script snippet in the screenshot that you shared didn’t come from that script, so the change that you made actually introduces a bug because it’s not related to the rest of the code.
Which script above did you use as the basis for your setup?
I think that there may be some confusion here. There have been multiple scripts shared in this thread, and the tip from @Dominik_Bosnjak is related to line 116 in the latter of the two—the post by @Theo_Michel in September of 2020—which looks like this:
if(foundItem >= 0) {
The script snippet in the screenshot that you shared didn’t come from that script, so the change that you made actually introduces a bug because it’s not related to the rest of the code.
Which script above did you use as the basis for your setup?
Thanks. I was not aware there were two scripts on the page. I am using the script at the top of the page.
Do you think it would be better to use the 2nd one?
Could you clarify the difference between the two?
Thanks. I was not aware there were two scripts on the page. I am using the script at the top of the page.
Do you think it would be better to use the 2nd one?
Could you clarify the difference between the two?
Wow, good call @Justin_Barrett , I was debugging the wrong thing weeks apart.
I’ll take another look in a bit, but as far as merit goes, the second script utilizes recursion, takes advantage of prototypal inheritance, and is nicely formatted to top it off so that I can actually see what’s going on even through a Friday migraine.
Basically, some of JavaScript’s biggest strengths utilized in 130 lines of code.
The other effort, the one you’re trying to get to work, apparently, is tenacious, but clearly written by someone less experienced; possibly someone who only picked up programming with JavaScript. Just my 2 cents on
How often do you need this to run? And are you sure you want it to be an automation? A base with a couple thousand records in it and you’re one missing semicolon away from filling your monthly automation quota within hours. Unless you’re only running a single automation per base and that sole isn’t in charge of both creation and deletion, that 100k cap is never too safe.
And you know, we had a lot of missing semicolons here by now haha.
Wow, good call @Justin_Barrett , I was debugging the wrong thing weeks apart.
I’ll take another look in a bit, but as far as merit goes, the second script utilizes recursion, takes advantage of prototypal inheritance, and is nicely formatted to top it off so that I can actually see what’s going on even through a Friday migraine.
Basically, some of JavaScript’s biggest strengths utilized in 130 lines of code.
The other effort, the one you’re trying to get to work, apparently, is tenacious, but clearly written by someone less experienced; possibly someone who only picked up programming with JavaScript. Just my 2 cents on
How often do you need this to run? And are you sure you want it to be an automation? A base with a couple thousand records in it and you’re one missing semicolon away from filling your monthly automation quota within hours. Unless you’re only running a single automation per base and that sole isn’t in charge of both creation and deletion, that 100k cap is never too safe.
And you know, we had a lot of missing semicolons here by now haha.
huh… A Masters in Computer Science, 20+ years of professional programming across atleast 5 different languages, built systems supporting millions of users, built one of the more popular Airtable products in their ecosystem, and still have a long ways to go to catch up to your skills. Thanks for keeping me humble Dominik, I always need the reminder :winking_face:
huh… A Masters in Computer Science, 20+ years of professional programming across atleast 5 different languages, built systems supporting millions of users, built one of the more popular Airtable products in their ecosystem, and still have a long ways to go to catch up to your skills. Thanks for keeping me humble Dominik, I always need the reminder :winking_face:
Nah, we’re about even if I also count my years of experience in eating my own words, that “I’m an idiot” feeling is quintessential programming imo, more than any technology or trend can ever hope to be. And I’m yet to encounter someone who codes – maybe for a living, maybe just because, emphasis on the present tense – and is in need of a humbling haha.
Case in point: you not automatically assuming I meant the other person because my comment doesn’t make sense otherwise.
Whereas in reality, it’s just an indexing issue .
And if Theo(dore?) frequents these forums, I’m now really curious how accurate I’ve been overall.
Scripting has undergone a lot of changes since this thread was started.
I decided to write a new version of the script to take advantage of some new features, such as script settings, that lets the script remember the table and fields without having to touch the code.
This version has a few differences from the original and is entirely my own code, although aspects of the algorithm are similar.
- uses script settings for setting table & fields (which did not exist before)
- works on only one table, not multiple tables
- does not allow limiting records to a view
- various speed improvements and error checking
This script is best used as a batch update when first setting up back links. I also have a matching automation script for when either the link or the backlink is updated, without needing to store previous versions of links. If you are interested in the automation script, please book an appointment with me.
Wow, good call @Justin_Barrett , I was debugging the wrong thing weeks apart.
I’ll take another look in a bit, but as far as merit goes, the second script utilizes recursion, takes advantage of prototypal inheritance, and is nicely formatted to top it off so that I can actually see what’s going on even through a Friday migraine.
Basically, some of JavaScript’s biggest strengths utilized in 130 lines of code.
The other effort, the one you’re trying to get to work, apparently, is tenacious, but clearly written by someone less experienced; possibly someone who only picked up programming with JavaScript. Just my 2 cents on
How often do you need this to run? And are you sure you want it to be an automation? A base with a couple thousand records in it and you’re one missing semicolon away from filling your monthly automation quota within hours. Unless you’re only running a single automation per base and that sole isn’t in charge of both creation and deletion, that 100k cap is never too safe.
And you know, we had a lot of missing semicolons here by now haha.
Could you please point out where the recursion and inheritance occur?
I’m not seeing either.
I also find a few things in the script to be rather curious, such as line 67:
if(!currentAddedKidParents.includes( { id: modifiedRecord.id, name: modifiedRecord.name })) {
I bet this line does not do what the author intended. This condition will always evaluate as true. When using .includes()
with an array of objects, JavaScript checks if the objects themselves are included, not the contents of the object. As written, the newly created object will never be included in the array, even if the array has an object with the same property values. Thus the includes()
will always be false, and the !
negates that , making the condition always true.
There are a few other things in the script that imply that the author had limited experience with Airtable scripting when he wrote the script. For example, every pair of updateRecordAsync
calls could be condensed into a single call, which would cut the run time almost in half. (And there are several other changes that could speed up the script, such as not having updateRecordAsync
inside a loop.)
Reply
Enter your E-mail address. We'll send you an e-mail with instructions to reset your password.