Exception: Bad value (line 9, file "Code") - firebase

I want to read my data from my firebase and write it in google sheet. When i run this Apps Script I got the error
Exception: Bad value (line 9, file "Code")
var ss= SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range=sheet.getRange(1,1,4,2);
var data= getFirebaseData("Entries");
Logger.log(data)
range.setValues(JSON.parse(data))
}
function getFirebaseData(data){
var firebaseUrl = "**";
var secret = "**";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl,secret);
var result = base.getData(data);
return result;
}
the data in Firebase is like this:
Entries
date:"09/09/2020"
docAmount:88
docNo:55
partyName: "ffg"
Solutions that I might accept,(solve this issue)or (show a better way to do the process)

You want to write an object's (data) key/value pairs to two columns in a sheet. setValues accepts a 2D array, not an object, so you should first transform data to a 2D array:
const array = Object.keys(data).map(key => [key, data[key]]);
sheet.getRange(1, 1, array.length, array[0].length).setValues(array);
Note:
Making the range dimensions dependent on the array dimensions (array.length, array[0].length) will give you more flexibility (what if the object has 5 properties instead of 4?).
This will only work for simple objects, not if they have nested properties.
Reference:
setValues(values)
Object.keys()
getData(path, optQueryParameters)

Related

Read Data from Firebase to Sheets

I'm having trouble splitting the data once I have retrieved it using .getData().
The firebase data is in the form:
mainTag: subTag1:"["Test1",70,0,18]", subTag2:"["Test2",65,2,18]", etc...
This is as far as I've managed to get as I'm not sure of the format of the data. I can't directly set it to the value of a single cell or a range of cells because the parameters don't match the method signature for the sheet. I've tried splitting the data as if it were an object and as if it were a string but keep getting nulls logged in the logger so I'm not sure what to do with it.
function getData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("TestSheet");
var range = sheet.getRange("A1:D2");
var firebaseUrl = "https://.....firebaseio.com/";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
var data = base.getData("mainTag");
//range.setValues(data);
Logger.log(data);
Logs: {subTag2=["Test2",65,2,18], subTag1=["Test1",70,0,18]}
}
My objective is to set the value of 4 cells in a row to the data inside each subtag. (A1="Test1", B1=70, C1=0, D1=18) Then subtag2 is in row 2 and so on. I haven't got to splitting the data yet as I'm not sure how to format the data from firebase for it to be able to be used in .setValues()
You need to loop through the subtags and access the values belonging to each key
This can be best achieved with Object.keys().
Sample:
var data = base.getData("mainTag");
var array = [];
for(var i in data) {
var newdata = Object.keys(data[i]).map(function (key) {
return data[i][key];
});
array.push(newdata);
}
var range = sheet.getRange(1,1, array.length, array[0].length);
range.setValues(array);

Firebase RealtimeDB to Google Sheets through AppScript

I am trying to write Firebase RealtimeDB data into a Google Sheets spreadsheet through Google Appscript.
This is what my RealtimeDB looks like:
My goal is
I have tried the following code snippet:
function writeSheets() {
var firebaseUrl = "https://<mydatabase>.firebaseio.com/Attendees";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
var data = base.getData();
var ss = SpreadsheetApp.openById("<mySheet>");
var sheet = ss.getSheetByName("Sheet1");
var num = 2;
range = ss.getRange("A"+num+":B"+num+"");
for(var i in data) {
var values = [[ data[i].ID, data[i].Name ] ];
range.setValues(values);
num += 1;
range = sheet.getRange("A"+num+":B"+num+"");
}
console.log(JSON.stringify(data));
}
It seems to print empty cells into Google Sheet. Not exactly sure why.
Any input would be greatly appreciated. Thanks.
EDIT:
Console Log :
[20-04-08 20:46:26:102 HKT] TypeError: Cannot read property 'ID' of null
at writeSheets(Code:13:27)
There is a extra null in the data. Filter it out and map the array of objects to a 2D array and setValues the 2D array:
const data = [null,{"ID":1001,"Name":"Bob"},{"ID":1069,"Name":"Steve"},{"ID":1420,"Name":"Bill"}];
const out = data.filter(Boolean).map(Object.values);
console.log(out);
/*SetValues the entire array*/
//sheet.getRange(1,1,out.length, out[0].length).setValues(out);

How to separate multiple columns from a range in an array?

I have a range of data in a Google Sheet and I want to store that data into an array using the app script. At the moment I can bring in the data easily enough and put it into an array with this code:
var sheetData = sheet.getSheetByName('Fruit').getRange('A1:C2').getValues()
However, this puts each row into an array. For example, [[Apple,Red,Round],[Banana,Yellow,Long]].
How can I arrange the array by columns so it would look: [[Apple,Banana],[Red,Yellow],[Round,Long]].
Thanks.
It looks like you have to transpose the array. You can create a function
function transpose(data) {
return (data[0] || []).map (function (col , colIndex) {
return data.map (function (row) {
return row[colIndex];
});
});
}
and then pass the values obtained by .getValues() to that function..
var sheetData = transpose(sheet.getSheetByName('Fruit').getRange('A1:C2').getValues())
and check the log. See if that works for you?
Use the Google Sheets API, which allows you to specify the primary dimension of the response. To do so, first you must enable the API and the advanced service
To acquire values most efficiently, use the spreadsheets.values endpoints, either get or batchGet as appropriate. You are able to supply optional arguments to both calls, and one of which controls the orientation of the response:
const wb = SpreadsheetApp.getActive();
const valService = Sheets.Spreadsheets.Values;
const asColumn2D = { majorDimension: SpreadsheetApp.Dimension.COLUMNS };
const asRow2D = { majorDimension: SpreadsheetApp.Dimension.ROWS }; // this is the default
var sheet = wb.getSheetByName("some name");
var rgPrefix = "'" + sheet.getName() + "'!";
// spreadsheetId, range string, {optional arguments}
var single = valService.get(wb.getId(), rgPrefix + "A1:C30");
var singleAsCols = valService.get(wb.getId(), rgPrefix + "A1:C30", asColumn2D);
// spreadsheetId, {other arguments}
var batchAsCols = valService.batchGet(wb.getId(), {
ranges: [
rgPrefix + "A1:C30",
rgPrefix + "J8",
...
],
majorDimension: SpreadsheetApp.Dimension.COLUMNS
});
console.log({rowResp: single, colResp: singleAsCols, batchResponse: batchAsCols});
The reply will either be a ValueRange (using get) or an object wrapping several ValueRanges (if using batchGet). You can access the data (if any was present) at the ValueRange's values property. Note that trailing blanks are omitted.
You can find more information in the Sheets API documentation, and other relevant Stack Overflow questions such as this one.

How should I structure my data so it works with firebase?

I want to update some data in the forms of
wordBank = {
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
}
the goal is to able to extract and display all the values of all the keys in each JSON object. How do I create this format on firebase? do I use .update? or something else?
currently I could only get firebase .update() to work with an array but it gives me data like this
wordBank = [
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
];
where each word-object is an index in the array.
Here's how I construct my wordObjects:
function getWords() {
if (document.getElementsByClassName("vortarobobelo").length != 0){
var words;
words = document.getElementsByClassName("vortarobobelo")[0].children[0].children;
for (var i =0; i < words.length; i++) {
var localBank = {} //creating the local variable to store the word
var newWord = words[i].children[0].innerText; // getting the word from the DOM
var newTranslation = words[i].children[1].innerText; // getting the translation from the DOM
localBank.word = newWord;
localBank.translation = newTranslation;
localBank.count = 0 //assuming this is the first time the user has clicked on the word
console.log(localBank);
wordBank[localBank.word] = localBank;
fireBank.update(localBank);
}
}
}
If you want to store the items within an object, you need to pick keys to store them against.
You can't store unkeyed values inside an object in Javascript. This would result in a syntax error:
wordBank = {
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
}
The other option is to store them in an array, in which case the keys will be automatically assigned as array indices. Just like your second example.
Maybe you want to store the word objects, using the word itself as a key?
wordBank = {
aprobi: {word:"aprobi", translation:"to approve", count:2},
bati: {word:"bati", translation:"to hit, to beat, to strike", count:1},
da: {word:"da", translation:"of", count:1}
}
This would be easy to do with Firebase. Let's say you have all of your word objects as a list.
var ref = new Firebase("your-firebase-url");
wordObjects.forEach(function(wordObject) {
ref.child(wordObject.word).set(wordObject);
});
Or you could create the object with Javascript, then add it to Firebase using .update.
var wordMap = {};
wordObjects.forEach(function(wordObject) {
wordMap[wordObject.word] = wordObject;
});
ref.update(wordMap);

Accessing properties of javascript objects using type dynamic in C# 4

I define a com object as a dynamic type in c# I am able to call methods quite easily.
However when I try to access a property on the same object I get an invalid cast exception.
The object in question is an array, passed to managed code from JavaScript, and I wish to get the length property of it as an int.
I know I am missing something odd because I am not getting a 'does not contain a definition' exception and I can access the property easily using reflection/InvokeMember.
Why can I not convert the length property of the dynamic type to an int?
For example
This Fails
dynamic com = comObject;
int i = com.length; // RTBE here.
This Works
Type type = comObject.GetType();
int i = (int)type.InvokeMember("length", BindingFlags.GetProperty, null, comObject, null);
* Update *
After a lot of testing I have narrowed this oddness to cases of multi-dimensional arrays.
The com object in question is a parameter passed from a html document to managed code. For all intents and purposes the object sometimes looks like this in JavaScript.
var x = ["a1", "a2", "a3"];
When an array like this comes to managed code I am able to get the length AOK using the type dynamic. (i.e. the first example here that fails actually works). However, if it is a multi-dimensional array such as the following structure in JavaScript.
var y = [["b1", "b2", "b3"], "a2", "a3"];
Then I get an error when trying to access its length property dynamically. Note, I can still access the length via reflection in this case. It seems to me that for some reason the length property does not get correctly mapped when a multidimensional array is used as a dynmaic type...
In my case what I have done to solve(!?) this is add a 'length_' property to the array like so before passing it.
var y = [["b1", "b2", "b3"], "a2", "a3"];
y.length_ = y.length;
Now in managed code I can accesses this property as expected without error. Far from ideal but seems to work...
dynamic com = comObject;
int i = com.length_; // 3!
Further Update
Ok, so it seems that as well as the length property the objects index gets lost to the dynamic type as well. Again it is accessible via reflection though...
Fails
dynamic com = comObject; // js array i.e. var x = [1, 2];
int i = com[0]; // MissingMemberException - Error while invoking [PROPERTYGET, DISPID(0)].
int i = com["0"]; // MissingMemberException - Error while invoking [PROPERTYGET, DISPID(0)].
Works
Type type = comObject.GetType();
int i = (int)type.InvokeMember("0", BindingFlags.GetProperty, null, comObject, null); // 1
In simple terms you can't access the length property of a multi-dimensional array in c# via the type dynamic unless, it seems, you have used the length property in JavaScript first...
The simple test below shows this very clearly. I hope this saves someone else the head scratching I have been having over the last day or so.
[ComVisibleAttribute(true)]
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
webBrowser1.ObjectForScripting = this;
StringBuilder html = new StringBuilder();
html.Append("<script>");
html.Append("var arr1 = [1, 2, 3, 4];");
html.Append("var arr2 = [arr1, 2, 3, 4];");
html.Append("var fn1 = function() { return arr1; };");
html.Append("var fn2 = function() { return arr2; };");
html.Append("var fn3 = function() { alert(arr2.length); }");
html.Append("</script>");
webBrowser1.DocumentText = html.ToString();
webBrowser1.DocumentCompleted += (o, e) =>
{
dynamic arr1 = webBrowser1.Document.InvokeScript("fn1");
int i = arr1.length;
MessageBox.Show(i.ToString()); //4
// If I call fn3 here then the arr2.length *is* available as int i2 below!
////webBrowser1.Document.InvokeScript("fn3"); // 4
dynamic arr2 = webBrowser1.Document.InvokeScript("fn2");
int i2 = arr2.length;
MessageBox.Show(i2.ToString()); // unless fn3 is called you get...
/*
System.MissingMemberException was unhandled by user code
Message=Error while invoking length.
Source=System.Dynamic
StackTrace:
at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
at CallSite.Target(Closure , CallSite , ComObject )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
*/
};
}
}
update
It seems (see comments) that this behaviour is fixed if the WebBrowser control uses version 9 of Internet Explorer (...the control uses the version of IE on the machine). I can only presume that the IE9 'Chakra' JavaScript engine is doing something extra/different to the old js engine in this case.

Resources