I am have a query data and want to create a manual array of structs with it, i am using this code but it seems wrong. what i am missing here what should do it here
for(i in myquery) {
st['Contact'][#i#]['ID'] = i.id;
st['Contact'][#i#]['Name'] = i.name;
st['Contact'][#i#]['Email'] = i.email;
}
You have some issues in your code. I'm going to break this down:
Let's assume you have a query result like the follwing:
myquery= queryNew(
"id, name, email",
"integer,varchar,varchar",
[
{"id":43,"name":"Antonio", "email": "antonio#somedomain.com"},
{"id":65,"name":"Manuel", "email": "manuel#somedomain.com"},
{"id":77,"name":"Paula", "email": "paula#somedomain.com"},
{"id":87,"name":"Jennifer", "email": "Jennifer#somedomain.com"}
]);
Then iterating (looping) through your query with a for( row in query ) like you did won't work:
for( i in myquery ){
st['Contact'][#i#]['ID'] = i.id;
st['Contact'][#i#]['Name'] = i.name;
st['Contact'][#i#]['Email'] = i.email;
}
The cause:
When using a for( row in myquery ) you are iterating the query by row. But in your loop you want to make use of an index i to set your arrays. Then, i is not going to be a numeric integer that you are expecting, but a complete rowset. It will try using the complete row as index, and that shouldn't work. If you need an index, you MUST also iterate your query by index, e.g. with for (i = 1; i <= myquery.recordCount; i++). As a reference please see how to iterate a query by index see cfdocs here, or alternatively use the ArrayAppend() or append() member without using any index.
You need to pre define st['Contact'] as an array with st['Contact']=[]; first, otherwise CF will assume it's a struct
Also, you are setting the array with i, but outputting it with [#i#] without any quotes. Use # in outputs embraced with quotes like this: st['Contact']['#i#']['ID'] or without embracing quotes like this: st['Contact'][i]['ID']. But not the like this st['Contact'][#i#]....
Correcting your code to make st.Contact be populated as an array, it could be similar to:
Alternative 1: Use ArrayAppend() or append() member function:
st['Contact']=[];
for ( i in myquery ) {
st['Contact'].append(
{"id": myquery.id,
"name": myquery.name,
"email": myquery.email
});
}
same but simplier:
st['Contact']=[];
for ( i in myquery ) {
st['Contact'].append( i );
}
Alternative 2: Iterate the query by index with for (i = 1; i <= myquery.recordCount; i++)
st['Contact']=[];
for (i = 1; i <= myquery.recordCount; i++) {
st['Contact'][i]['ID'] = myquery.id[i];
st['Contact'][i]['Name'] = myquery.name[i];
st['Contact'][i]['Email'] = myquery.email[i];
}
If you dump the variable st with the help of the query I've submitted, you'll get the following data structure (here it's simply stringified):
{"Contact":[
{"Name":"Antonio","ID":43,"Email":"antonio#somedomain.com"},
{"Name":"Manuel","ID":65,"Email":"manuel#somedomain.com"},
{"Name":"Paula","ID":77,"Email":"paula#somedomain.com"},
{"Name":"Jennifer","ID":87,"Email":"Jennifer#somedomain.com"}
]}
Here is the complete cfscript code of both alternatives:
myquery= queryNew(
"id, name, email",
"integer,varchar,varchar",
[
{"id":43,"name":"Antonio", "email": "antonio#somedomain.com"},
{"id":65,"name":"Manuel", "email": "manuel#somedomain.com"},
{"id":77,"name":"Paula", "email": "paula#somedomain.com"},
{"id":87,"name":"Jennifer", "email": "Jennifer#somedomain.com"}
]);
st['Contact']=[];
for ( i in myquery ) {
st['Contact'].append(
{"id": myquery.id,
"name": myquery.name,
"email": myquery.email
});
}
dump(st);
// simplier version of above
st['Contact']=[];
for ( i in myquery ) {
st['Contact'].append( i );
}
dump(st);
st['Contact']=[];
for (i = 1; i <= myquery.recordCount; i++) {
st['Contact'][i]['ID'] = myquery.id[i];
st['Contact'][i]['Name'] = myquery.name[i];
st['Contact'][i]['Email'] = myquery.email[i];
}
dump(st);
Related
I'm not sure why I'm having such a hard time finding an answer for this, but I have a list that I need to get the value from where the key matches certain criteria. The keys are all unique. In the example below, I want to get the color where the name equals "headache". Result should be "4294930176".
//Example list
String trendName = 'headache';
List trendsList = [{name: fatigue, color: 4284513675}, {name: headache, color: 4294930176}];
//What I'm trying
int trendIndex = trendsList.indexWhere((f) => f.name == trendName);
Color trendColor = Color(int.parse(trendsList[trendIndex].color));
print(trendColor);
Error I get: Class '_InternalLinkedHashMap' has no instance getter 'name'. Any suggestions?
EDIT:
Here's how I'm adding the data to the list, where userDocuments is taken from a Firestore collection:
for (int i = 0; i < userDocument.length; i++) {
var trendColorMap = {
'name': userDocument[i]['name'],
'color': userDocument[i]['color'].toString(),
};
trendsList.add(trendColorMap);
}
I guess, I got what the problem was. You were making a little mistake, and that was, you're trying to call the Map element as an object value.
A HashMap element cannot be called as f.name, it has to be called f['name']. So taking your code as a reference, do this, and you are good to go.
String trendName = 'headache';
List trendsList = [{'name': 'fatigue', 'color': 4284513675}, {'name': headache, 'color': 4294930176}];
//What I'm trying
// You call the name as f['name']
int trendIndex = trendsList.indexWhere((f) => f['name'] == trendName);
print(trendIndex) // Output you will get is 1
Color trendColor = Color(int.parse(trendsList[trendIndex]['color'])); //same with this ['color'] not x.color
print(trendColor);
Check that out, and let me know if that helps you, I am sure it will :)
I am using .indexOf() to parse an array in a separate sheet via url and find a string in a header to return a numerical value or -1. Issue is I need to find the string match that is non case sensitive. ie var targetEmail = targetHeader.indexOf("Email"); would need to match "email", "user email", User email", etc. Normally I would use a regex but can't find a way to do this in Apps Script. Please any help would be very much appreciated.
sample code below:
var userHeader = USER.getRange(1, 1, 1, USER.getLastColumn()).getValues()[0];
var targetHeader = importSheet.getRange(1, 1, 1, importSheet.getLastColumn()).getValues()[0];
var ui = SpreadsheetApp.getUi();
var targetEmail = targetHeader.indexOf("Email");
if(targetEmail == -1)
ui.alert("Can not find Email column. Please rename column in source sheet.");
Using Javascript RegExp, you can use the following
let targetEmail = targetHeader.findIndex(h => /email/gi.test(h));
if(targetEmail == -1)
ui.alert("Can not find Email column. Please rename column in source sheet.");
Note: findIndex, let and the function shorthand will only work if your script is using the new V8 runtime.
I personally prefer to name the column I want using Google Sheets named ranges, and then use getNamedRanges(), so I'll be able to find the column number by its range name, regardless of the column heading text. If columns are inserted or deleted, the named range still points to the column you're looking for.
You could solve for this by
(1) joining the array into a string
targetHeader.join()
(2) and then changing the string to lowercase
toLowerCase()
and then looking for "email" (lower case).
var targetHeader = importSheet.getRange(1, 1, 1, importSheet.getLastColumn()).getValues()[0];
targetHeader = targetHeader.join().toLowerCase();
var targetEmail = targetHeader.indexOf('email');
Edit:
To find the column number with the word 'email' in it:
var importSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet101');
var targetHeader = importSheet.getRange(1, 1, 1, importSheet.getLastColumn()).getValues()[0];
var flag = false;
for (var c = 0; c < targetHeader.length; c++) {
var cellValue = targetHeader[c].toLowerCase();
if (cellValue.indexOf('email') > -1) {
flag = true;
break;
}
}
if (!flag) {
// ui alert that email not found
} else {
var columnWithEmalInHeader = c + 1;
// column with value 'email' found. Column number is c+1
// first time match of 'email' will pop-up here. All subsequent amounts will be ignored.
}
I am having trouble executing the Spreadsheet script below.
I think there are two mistakes but I do not know how to fix.
Could anyone help it to fix it?
1:Wildcard
if(original_date=='....-..-..')
2:if synteax
if(original_date=='....-..-..')
{condition="matched"}
Detail
On the spreadsheet, there are two columns.
The first columns have dates in a format as YYYY-MM-DD such as 2020-04-21.
But sometimes, they have different formats such as 04/21/2020.
The second columns are empty.
Only when the first column cell has the "YYYY-MM-DD" format, I want to copy the cell into the second cell in the second column.
*They have 10 rows.
Here is the script.
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1')
for(let i=1; i<=10; i++)
{
original_date_range = sheet.getRange(i, 1);
original_date = original_date_range.getValue();
cleaned_date_range = sheet.getRange(i, 2);
var condition = "";
if(original_date=='....-..-..')
{condition="matched"}
switch(condition)
{
case "matched":
cleaned_date_range.setValue(original_date);
case "":
cleaned_date_range.setValue("");
break;
}
}
I found the solution by myself.
1.Wildcard
The date "2020-04-25" can be written as /\d{4}.\d{2}.\d{2}/
2.If syntax
(Wildcard).test(string to check) is going to give you true/false output
In summary,
if you want to execute different tasks through the verification of date format such as 2020-04-25,
var sheet1 = SpreadsheetApp.getActive().getSheetByName('Sheet1')
var limit = sheet1.getLastRow()
for(let i=4; i<=10; i++) //You can add more rows if you have
{
var orignal_date_cell = sheet1.getRange(i, 1);
var orignal_date_cell_value = orignal_date_cell.getValue();
var target_date_cell = sheet1.getRange(i,2);
if ((/\d{4}.\d{2}.\d{2}/).test(orignal_date_cell_value))
{
target_date_cell.setValue(orignal_date_cell_value);
}
}
I have this body request example:
{
"users": [{
"userId": 123
}, {
"userId": 1234
}]
}
For the previous example I receive one std::list<UsersId>* VUsers that have my userId (in this case '123' and '1234'), create cJSON array, iterate my list and get all userId. (Note: the UsersId is one auxiliar class that I use and receive one int in constructor)
cJSON* cJsonUsers = cJSON_CreateArray();
cJSON_AddItemToObject(root, "VUsers", cJsonUsers);
std::list<UsersId>::const_iterator itUsers = VUsers->begin();
while (itUsers != VUsers->end())
{
cJSON *cJsonVNode = cJSON_CreateObject();
cJSON_AddItemToArray(cJsonUsers, cJsonUser);
cJSON_AddNumberToObject(cJsonUser, "userId", itUsers->userId);
++itVNodes;
}
But know I want to the same but make more simple/easy and need to change the body request to something like this:
{
"users": {
"userId": [123, 1234]
}
}
I'm using this c++ library -> https://github.com/DaveGamble/cJSON but I dont understand how to do to implement the modification that I need.
EDIT 2 (PARSE THE JSON)
cJSON* cJsonUsers = cJSON_GetObjectItem(root, "users");
if (!cJsonUsers) return 0;
if (cJsonUsers->type != cJSON_Array) return 0;
std::list<VUserId>* users = new std::list<VUserId>();
cJSON* cJsonVUser;
cJSON_ArrayForEach(cJsonVUser, cJsonUsers)
{
cJSON* cJsonVUserId = cJSON_GetObjectItem(cJsonVUser, "userId");
if (!cJsonVUserId) continue;
int user_id = cJsonVUserId->valueint;
VUserId userId(user_id);
users->push_back(userId);
}
Something like this could work, that is, create the object and array outside of the loop, and insert the numbers inside the loop:
cJSON* cJsonUsers = cJSON_CreateObject();
cJSON_AddItemToObject(root, "users", cJsonUsers);
cJSON* cJsonUserId = cJSON_CreateArray();
cJSON_AddItemToObject(cJsonUsers, "userId", cJsonUserId);
std::list<UsersId>::const_iterator itUsers = VUsers->begin();
while (itUsers != VUsers->end())
{
cJSON_AddItemToArray(cJsonUserId, cJSON_CreateNumber(itUsers->userId));
++itVNodes;
}
Note that there are languages out there that are more convenient to manipulate JSON if you have the flexibility (disclaimer: I was involved in the design of some of these). Of course there are always use cases when you have to use C++ and in which a library makes a lot of sense.
With languages such as C++ or Java, there is an impedance mismatch between objects in the classical sense, and data formats like XML or JSON. For example, with the standardized, declarative and functional XQuery 3.1 this does not need much code to transform the first document into the second:
let $original-document := json-doc("users.json")
return map {
"users" : map {
"userId" : array { $original-document?users?*?userId }
}
}
A simple version of my document document is the following structure:
doc:
{
"date": "2014-04-16T17:13:00",
"key": "de5cefc56ff51c33351459b88d42ca9f828445c0",
}
I would like to group my document by key, to get the latest date and the number of documents for each key, something like
{ "Last": "2014-04-16T16:00:00", "Count": 10 }
My idea is to to do a map/reduce view and query setting group to true.
This is what I have so far tried. I get the exact count, but not the correct dates.
map
function (doc, meta) {
if(doc.type =="doc")
emit(doc.key, doc.date);
}
reduce
function(key, values, rereduce) {
var result = {
Last: 0,
Count: 0
};
if (rereduce) {
for (var i = 0; i < values.length; i++) {
result.Count += values[i].Count;
result.Last = values[i].Last;
}
} else {
result.Count = values.length;
result.Last = values[0]
}
return result;
}
You're not comparing dates... Couchbase sorts values by key. In your situation it will not sort it by date, so you should do it manually in your reduce function. Probably it will look like:
result.Last = values[i].Last > result.Last ? values[i].Last : result.Last;
and in reduce function it also can be an array, so I don't think that your reduce function always be correct.
Here is an example of my reduce function that filter documents and leave just one that have the newest date. May be it will be helpful or you can try to use it (seems it looks like reduce function that you want, you just need to add count somewhere).
function(k,v,r){
if (r){
if (v.length > 1){
var m = v[0].Date;
var mid = 0;
for (var i=1;i<v.length;i++){
if (v[i].Date > m){
m = v[i].Date;
mid = i;
}
}
return v[mid];
}
else {
return v[0] || v;
}
}
if (v.length > 1){
var m = v[0].Date;
var mid = 0;
for (var i=1;i<v.length;i++){
if (v[i].Date > m){
m = v[i].Date;
mid = i;
}
}
return v[mid];
}
else {
return v[0] || v;
}
}
UPD: Here is an example of what that reduce do:
Input date (values) for that function will look like (I've used just numbers instead of text date to make it shorter):
[{Date:1},{Date:3},{Date:8},{Date:2},{Date:4},{Date:7},{Date:5}]
On the first step rereduce will be false, so we need to find the biggest date in array, and it will return
Object {Date: 8}.
Note, that this function can be called one time, but it can be called on several servers in cluster or on several branches of b-tree inside one couchbase instance.
Then on next step (if there were several machines in cluster or "branches") rereduce will be called and rereduce var will be set to true
Incoming data will be:
[{Date:8},{Date:10},{Date:3}], where {Date:8} came from reduce from one server(or branch), and other dates came from another server(or branch).
So we need to do exactly the same on that new values to find the biggest one.
Answering your question from comments: I don't remember why I used same code for reduce and rereduce, because it was long time ago (when couchbase 2.0 was in dev preview). May be couchbase had some bugs or I just tried to understand how rereduce works. But I remember that without that if (r) {..} it not worked at that time.
You can try to place return v; code in different parts of my or your reduce function to see what it returns on each reduce phase. It's better to try once by yourself to understand what actually happens there.
I forget to mention that I have many documents for the same key. In fact for each key I can have many documents( message here):
{
"date": "2014-04-16T17:13:00",
"key": "de5cefc56ff51c33351459b88d42ca9f828445c0",
"message": "message1",
}
{
"date": "2014-04-16T15:22:00",
"key": "de5cefc56ff51c33351459b88d42ca9f828445c0",
"message": "message2",
}
Another way to deal with the problem is to do it in the map function:
function (doc, meta) {
var count = 0;
var last =''
if(doc.type =="doc"){
for (k in doc.message){
count += 1;
last = doc.date> last?doc.date:last;
}
emit(doc.key,{'Count':count,'Last': last});
}
}
I found this simpler and it do the job in my case.