Airtable Scripting block - Batch copy a field to another field in same table (10,000 records) - airtable

I'm trying to copy one field to another field in the same table with 10,000 + records, in batches of 50 using the Scripting App.
What am I doing wrong in this code block? It only copies the first record. If I remove the await, it'll copy 15 records then stop.
let table = base.getTable('Merchants');
let view = table.getView('Grid view');
let query = await view.selectRecordsAsync();
let records = query.records;
updateLotsOfRecords(records);
async function updateLotsOfRecords(records) {
let i = 0;
while (i < records.length) {
const recordBatch = records.slice(i, i + 50);
for (let record of recordBatch) {
let sourceValue = record.getCellValue('Merchant');
await table.updateRecordAsync(record, { 'LogoBase64': sourceValue });
}
i += 50;
}
}

you should use updateRecordsAsync function, not updateRecordAsync
When using single update function in loop, there is no sense to divide it into batches.
You exceed some limit of calls per second, that's why it stops.
For multiple updates, you need to use updateRecordsAsync, like this
while (recordsToWrite.length > 0) {
await updates.updateRecordsAsync(recordsToWrite.slice(0, 50));
recordsToWrite = recordsToWrite.slice(50);
}
Data that you should pass to it, more complex. I learned JS for 3 months and still have difficulties understandins all these "arrays of arrays of objects, passed via object's property". But that's the key to unerstand JS.
It's quite hard to leave basic/pascal habits, with plenty of inserted FOR loops, and GOTO sometimes))
I think, you already found the answer for 2 months, so my answer may be useless, but when i write it here, maybe i understand it better for myself. And help to some beginners also.
For single write, you pass (record, Object), where object is {field:'Value}
For multiple, you should pass
Array of Objects, where
Object is {id:recordID, fields:{object2}} , where
object2 is array of obj3 [ {obj3},{obj3}, {obj3} ], where
obj3 is a { 'Name or ID of field': fieldvalue }
you script might be:
let query = await view.selectRecordsAsync();
let updates=query.records.map(rec=>{
Map method can be applied for arrays, and 'query.records' is array of records. Here
'rec' is loop variable inside this "arrowfunction"
now let's create obj3 , in our case { 'Name or ID of field': fieldvalue }
{'LogoBase64':rec.getCellValue('Merchant')}
wrap it into fields property
fields:{'LogoBase64':rec.getCellValue('Merchant')}
and add record id
wrapping as Object.
To avoid complex string with linebreaks, and to make object creation easier, we can do it with function:
{rec.id, fields:{'LogoBase64':rec.getCellValue('Merchant')}}
fuction myObj(rec){return {rec.id, fields:{'LogoBase64':rec.getCellValue('Merchant')}}
map(rec=>myObj(rec)) - can be written as map(myObj)
we need array of objects, and map method gets first array, doing something with each element and return other array, of results. like we need.
and now finally we get
let table = base.getTable('Merchants');
let view = table.getView('Grid view');
let query = await view.selectRecordsAsync();
function myObj(rec){return {'id':rec.id,'fields':{'Logobase64':rec.getCellValue('Merchant')}}};
let updates=query.records.map(myObj);
while (updates.length > 0) {
await table.updateRecordsAsync(updates.slice(0, 50));
updates = updates.slice(50); }

Related

Update multiple Firebase documents using an array of Document IDs in Swift

I have an array of Strings which represent Firebase Document IDs like so:
var idArray = [“PuLDb90jgz3a5P8bLQoy”, “PMKoZIp46umXQnUlA64a”, “cVGbD3Wy4gWjZ9fZP7h1”]
This Array is dynamic and has been generated by a previous getDocuments call. It could have up to 15 ID strings in it, so it cannot be hard coded.
Within each Firebase Document I have an Int field called menuPosition set to a current value.
What I am trying to do is update each document which appears in the idArray and simply -1 from the menuPosition field value in one go.
What should be so straightforward is driving me crazy - does anyone know a simple way to do it? Obviously, if I put it in a for loop then the code will run too many times and my menuPosition value would be wrong. I just want each operation to perform once.
Any help would be greatly appreciated
Just run a loop over the document IDs and decrement the field in each iteration.
var idArray = ["J2LovReBF0v8F4e0RSBU", "UcW8tsgld2ZuUo92xfP8", "oHTJ4iO1NWCK7x1aryne"]
for docId in idArray {
Firestore.firestore().document("someCollection/\(docId)").updateData([
"menuPosition": FieldValue.increment(Int64(-1))
])
}
If you want this operation to be atomic then just wrap it in a batch write.
var idArray = ["J2LovReBF0v8F4e0RSBU", "UcW8tsgld2ZuUo92xfP8", "oHTJ4iO1NWCK7x1aryne"]
let db = Firestore.firestore()
let batch = db.batch()
for docId in idArray {
batch.updateData([
"menuPosition": FieldValue.increment(Int64(-1))
], forDocument: db.document("someCollection/\(docId)"))
}
batch.commit()

Returning Array of Objects shows only one object in next step

Ive been working on a code zap step to make a get call to an api endpoint, and then transform the response into key pair objects to pass to further steps in zapier.
var fileIds = [],
tempData = [],
newData = [],
obj = [];
fetch('zoho getClientById endpoint'+inputData.id)
.then(function(res) {
return res.json();
})
.then(function(json) {
tempData = json.response.result.Leads.row.FL;
for(var i = 0; i < tempData.length; i++ ){
tempVal = tempData[i].val;
newData = tempData[i].content;
let allData = {};
allData[tempVal] = newData;
obj.push(allData)
}
callback(null, obj);
}).catch(callback);
Above is more or less the code Im using. It works, except that when the array of objects comes out of the step, only the first object is available for steps after. Im not certain if this is because of the way Im handling it, or if it's something with how zapier works.
Edit: What's interesting is I can use the log statement to see results in the meta data, and it shows the full array of objects.
So, after a little extra reading, it seems that zapier will take each object in an array and try trigger another step with it. I updated my set up to just take the empty object I initially created and set a key with a value based off the value and content responses from my other response.

How avoid having double loop?

From healthkit, I will receive some datas, for example steps data.
I save in my server this datas. Il have then an array of datas:
1.- startDate, endDate, value
2.- startDate, endDate, value
etc
It can be very lot of values in my server.
Then I get the values in the Healthkit. I have lot of values. Values who are in the server and new values.
I want to upload to the server only the new values.
I do so:
for each value in my server {
for each value in healthkit{
if(startDate, endDate and value are not equal to the value in the server){
then save the value in the server
}
}
}
The algo will work, but it's very very slow. I can have lot of values in the two systems. Most of them the same in the two places.
Have you an idea how to do better?
I cannot save a flag in the healthKit.
I'm using ionic with angular 4 and typescript.
This answer assumes that the data on your server and the data from healthKit are sorted in the same way, or that you can sort them without affecting anything else.
For example you can sort your data on the server and the data from healthKit by StartDate, break ties by EndDate, then break ties by value.
Now you have two sorted arrays that you want to merge. The idea is to use the merge function of merge sort explained here.
You will end up with an array containing all the data without repetitions, which you can save on your server.
Edit:
void mergeArrays(int arr1[], int arr2[], int n1,
int n2, int arr3[])
{
int i = 0, j = 0, k = 0;
// Traverse both array
while (i<n1 && j <n2)
{
// Check if current element of first
// array is smaller than current element
// of second array. If yes, store first
// array element and increment first array
// index. Otherwise do same with second array
if (arr1[i] < arr2[j])
arr3[k++] = arr1[i++];
else if (arr2[j] < arr1[i])
j++;
else
j++,i++;
}
// Store remaining elements of first array (healthKit Array)
while (i < n1)
arr3[k++] = arr1[i++];
}
I think I need a bit more context to understand the problem. By "new" values, are you including entries that have been modified, or just new entries that have been added?
If you only need the new values, and they are being added to the end of the array on the client side, then you can just take them from the end of the array.
const new_count = healthkit.length - myserver.length, // number of new entries
index_start = healthkit.length - new_count, // index in healthkit array where new entries start
new_values = healthkit.slice(index_start); // new array containing only new entries
addNewValues(new_values);
Now you have the new values in a separate array, and you can update them to the server.
If it is the case that you do need to update values that have changed, you can iterate over both arrays (only once, and at the same time) and find the differences. I am going to assume that the entries are in the same index for both arrays. I'm also assuming the "value" key is the only one you want to compare. You can do the following to find the values that have been modified.
const modified_values = [];
myserver.forEach(function(entry, i) { // iterate over server array
let healthkit_entry = healthkit[i]; // get healthkit entry with same index
if(entry.value !== healthkit_entry.value) { // see if value has changed
modified_values.push(healthkit_entry); // if changed, add to modified array
}
});
updateModifiedValues(modified_values);
Now the modified_values array has all of the modified entries from the healthkit array.

Group TableView items from NSArray returned in JSON Response in Swift

I have a table view and I want to populate the table view with the values provided to me in a JSON response in the form of NSArray and I have used the code below to extract this NSArray:
dispatch_async(dispatch_get_main_queue(), {
print(json)
if let listDictionary = parseJSON["list"] as? NSArray {
print("LIST: \(listDictionary)")
}
})
In JSON response, I get the following array of two elements as shown below:
list = (
{
"is_cover" = 0;
"is_recommand" = 0;
name = "Helena's Signature Dish";
ord = 5;
price = "105.00";
remark = "Helena's special made dish is one of the most priced and delicious dishes in the whole world.";
thumb = "56c351887a0e0.jpg";
},
{
"is_cover" = 0;
"is_recommand" = 0;
name = "Pineapple Fried Rice";
ord = 6;
price = "110.00";
remark = "We have the most delicious pineapple rice in the whole world!";
thumb = "56c704e15da79.jpg";
}
);
Now I want to show the value "name", "price" and "remark" fields from each element in the array into a UITableView.
But I can't find a way to break the values for name, price and remark for each element and put them into an another array so they can be displayed in a table view somehow because each element contains a very long string and I only need to extract some values from this string. Can anyone please help me with this as I'm very new to Swift.
Thanks!
You don't need to make a second array, Only extarct particular json from array in cellForRowatIndexPath on dictinary by objectatindex method of array and now you can access.
You can achieve it by creating a model Class which contains only
those properties which you needed.
Once you get the response array from server , iterate though each
dictionary and create a Model Object.Add this model objects in the
Array.
Use this model object in the Array for applying data in your cell using the index
One more thing to add is always prefer using objects as it gives (.)dot syntax & autosuggestion, so there will be less chances of
making mistakes. As if you use Dictionary there will be more chances
of using incorrect key getting crash
I fixed this by putting values in "name", "price" and "remark" fields into another array using flatMap as shown below:
let nameArray = listArray.flatMap({ element in (element["name"] as? String)! })
And then I got something like:
Name's: (
"Helena's Special Dish",
"Pineapple Fried Rice"
)
and then in the tableView function for cellForRowAtIndexPath I grouped the values in tableview.

Can I make a record more flexible?

Can somebody give me an example of how to make inserting data into an F# record more flexible?
I often see examples using records like this:
type Employee = {mutable name:string; mutable id:string}
let data =
[{name = "Thomas";id = "000"};
{name = "Johny";id = "001"};
{name = "Lucky";id = "002"};
{name = "Don";id = "003"}
]
Can't we start with no data at all and insert the data into the record later?
(What I mean is without declaration of the value of the data like in the example, so for example: the program is running and asking us to insert the data)
Can we doing something like this with record?
If you're talking about specifying values of a record as they become available, then you need to make fields of the record option so that you can represent the fact that value is missing. I'll use immutable records, because this is more common in functional style:
type Employee = { Name:option<string>; ID:option<string> }
Now you can create a record with only ID and add name when the user enters it:
let empty = { Name = None; ID = Some 123 }
let name = // read name from user
let full = { empty with Name = name }
If you're talking about adding items to a list as they become available, then you have several options. The direct one is to write a recursive function that repeatedly reads record and builds a list:
let rec readData i records =
let name = // read name from user
if name <> "" then
// Create new record and add it to our list
let itm = { Name = name; ID = string i }
readData (i + 1) (itm::records)
else
// Return records that we collected so far in the right order
records |> List.rev
Alternatively, you can also use sequence expressions (see for example free Chapter 12 (PDF) of Real-World Functional Programming). If you user interaction involves waiting for events (e.g. mouse click), then you can still use this style, but you'd need to wrap everything in asynchronous workflow and use Async.AwaitEvent.
Are you saw you often saw an example like that?
I'd say that it is not very idiomatic in F# to use mutable records.
Immutability is a rather large subject
to explain in one answer here, but
briefly: immutability means that the
objects you create never change:
they stay the way they were at
creation. In the immutable world, when
you want to 'change' something, you
create a new one, and throw away the
old one.
Anyway, if I understand your question correctly, you are actually talking about mutating data, not the record. So, you could have:
let data = []
let data = {name = "Thomas";id = "000"} :: data
let data = {{name = "Johny";id = "001"} :: data
But in this case, you aren't really 'changing' data, you are just creating a new list each time and pointing data at it.

Resources