I have in my google sheets cell(A10) json object with price: {"bitcoin":{"usd":45258}}
How do I parse this price number and exctract it to do something?
Like for example in C10 cell I want to write: "=A10['bitcoin']['usd']*B10"
This function may help.
function getData(json, path) {
const obj = JSON.parse(json);
const keys = path.split('.');
let current = obj;
for( key of keys ){
current = current[key];
}
return current;
}
And you can write in C10 like this:
=getData(A10, "bitcoin.usd")*B10
Chain the property names with "."
Here is a sample. Feel free to make a copy and check it.👍
Related
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); }
In a Google Sheet I have a first tab with data entries in column A. I would like to assign a category to each entry in column B using an arrayformula. The category is supposed to be determined in the following way: If in the second tab one of the strings of a column matches the entry, the header of the column is supposed to be assigned as the category.
I have the feeling that there should be a way to do this with the query function, but can't figure it out.
This is an example sheet. I am happy to slightly change the setup of the sheet if the solution requires it but would like to avoid blowing up the sheet. Also I am interested in the simplest possible solution.
Approach
I would use a custom function in this case so that you can gain more control on the logic and structure of your sheet.
First you should change move the categories sheet into a rows structure since Google Sheets gets the values in that way.
Now let's build the function that extracts the data from the categories and assign every name to the corresponding one.
This is a custom function so I build the proper docs header so that it will be visible in your Spreadsheet context.
/**
* Assign the name the corresponding category.
*
* #param {input} the name to assign to the category
* #return The matching category
* #customfunction
*/
function MATCHCATEGORY(input) {
// if the input is from ARRAYFORMULA I will reiterate along the array with a map
if (input.map) {
return input.map(MATCHCATEGORY)
} else {
// Recursion base case
// I will get the categories rows
var rows = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CATEGORIES").getDataRange().getValues();
var category = "";
// Now I will map a string matching function along the rows
rows.map(row => {
let no_blanks = row.filter(value => value != "");
for (var i=1; i<no_blanks.length; i++) {
if (input.includes(row[i])) {
category = row[0];
break;
}
}
});
// Finally I return the category
return category
}
}
Now we can use our custom function inside an ARRAYFORMULA:
=ARRAYFORMULA(MATCHCATEGORY("ENTRY COLUMN RANGE"))
I'm trying to convert this map {[string1,string2]}
in to an array like this
[string1, string2]
in dart
A declaration of that kind in Dart is a Set(which is like a list but cannot have duplicates) of Lists, given that to get the first value you should just use
obj.first
(Sets are declared like maps but without any key)
This is a Set.
So you can do this for convert it to list:
Set<List<String>> map = {['string1','string2']};
List list = [];
map.forEach((k) {
k.forEach((item) => list.add(item));
});
as already mentioned in other answers this is a Set
you can easy convert it to List like this
var mySet = {['string1', 'string2']};
var list = mySet.expand((e) => e).toList();
print(list); // [string1, string2]
The 'map' you gave has one key (list of strings), which seems to be missing a value.
If your map looked something like this:
Map<List<String>, int> map = {[string1,string2]: 0};
Then you could get an Iterable of your keys (or values if you wish) with:
dynamic temp = map.keys //for the keys or
//or temp = map.values //for the values
You can further convert that Iterable into a List by calling the function toList() on it:
List<String> myList = temp.toList();
I hope this answered your question.
And if you are not sure what the type of your object is, use:
print(yourObject.runTimeType);
According to the documentation, it needs to follows the Form Post rules at: https://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4. When looking at that information it did not give me much to work with in terms of complex objects or maps.
Right now, If I have a list for example: Each item in the list needs to be stringified.
var params = {"list": [1,2,3]};
// needs to be stringed.
params["list"] = params["list"].map((item)=>item.toString()).toList();
Simple. Also all base items need to be a string as well
var params = {"number": 1, "boolean": true};
params = params.forEach((k,v)=> params[k].toString());
But how do we handle maps?
var params = {"map": {"a":1,"b":"foo","c":false,"d":[]}};
// ??
It seems that after testing in my app and in dart pad, you need to make sure everything is strings, so i am trying to come up with a way to effectively cover lists, maps, and maybe more complex objects for encoding.
var params = {};
params["list"] = [1,2,3];
params["number"] = 1;
params["boolean"] = true;
params["map"] = {"a":1,"b":"foo","c":false,"d":[]};
params.forEach((String key, dynamic value){
if(value is List){
params[key] = value.map((v)=>v.toString()).toList();
}else if(value is Map){
// ????
}else{
params[key] = value.toString();
}
//maybe have an additional one for custom classes, but if they are being passed around they should already have their own JSON Parsing implementations.
}
Ideally, the result of this would be passed into:
Uri myUri = new Uri(queryParameters: params);
and right now, while i solved the list issue, it doesn't like receiving maps. Part of me just wanted to stringify the map as a whole, but i wasn't not sure if there was a better way. I know that when someone accidentally stringified the array, it was not giving me: ?id=1&id=2 but instead ?id=%5B1%2C2%5D which was not correct.
I don't think there is any special support for maps. Query parameters itself is a map from string to string or string to list-of-strings.
Everything else need to be brought into this format first before you can pass it as query parameter.
A simple approach would be to JSON encode the map and pass the resulting string as a single query parameter.
So I have two arrays a name array and a values array they are a string and double respectively. I want to be able for a user to type in a textfield and to iterate though the name array until there is a match then output the value that has the same number as the name to be outputted
this is the code i have:
for(var i = 0; i<name.count; i++){
if name[i] == typeFood{
yieldOutput == percent[i]
}
}
First, use find to locate the location of the value in the name array, then use the returned index to look up the percent:
if let idx = find(name, typeFood) {
yieldOutput = percent[idx]
}
You can also combine the two actions together with map to declare an output variable using let:
if let foodPercentage = find(name, typeFood).map({ percent[$0] }) {
// foodPercentage will have a value here
}
else {
// not-found logic here
}