Getting empty value with dot walk for groupBy in GlideAggrigate - servicenow

I need to get the value for classname in below case. I am getting empty value. Let me know what I am missing. I need to find all distinct classes for every type.
var ga_type = new GlideAggregate('cmdb_rel_ci');
ga_type.groupBy('type');
ga_type.query();
if(ga_type.next()){
gs.log("Type : " + ga_type.type.getValue());
var ga_parent = new GlideAggregate('cmdb_rel_ci');
ga_parent.addQuery('type.sys_id', ga_type.type.getValue());
ga_parent.groupBy('parent.sys_class_name');
ga_parent.query();
var parent = [];
while(ga_parent.next()){
var p = {};
p.parentClassName = ga_parent.parent.sys_class_name.toString();
p.parentName = ga_parent.parent.name.toString();
gs.log("ParentClassName : " + p.parentClassName + " Parent Name : " + p.parentName);
parent.push(p);
}
}

As Tim says it's hard to know exactly what's being asked here, but it looks like you might be trying to get a list of all the types of relationships in cmdb_rel_ci. If that's the case, this should do it:
var count = new GlideAggregate('cmdb_rel_ci');
count.addAggregate('COUNT', 'type');
count.query();
var listOfParents = [];
while(count.next()){
var parent = count.type;
var parentCount = count.getAggregate('COUNT','type');
listOfParents.push(parent); //or parent.getDisplayValue()
gs.log(parent.getDisplayValue() + ": " + parentCount);
}
This is basically just the third example from the docs: GlideAggregate

Related

Sorting by 2 variables

This is a follow up to my previous question that was answered: How to Not Transfer Blank Column to Master Sheet?
2 new questions.
How do I sort by 2 items? I need to sort by Person and their Status (ie Completed). In the status cell is also a date, I just want it to find the completed part.
How to skip if the status is completed? Both of these are for the page that has already been combined. Hope this makes sense.
Sample Sheet: https://docs.google.com/spreadsheets/d/1Y48N8W8CdaNlrxXopj5lnHTiNep33g_qA2-kTrPdt3A/edit#gid=130911536
function combineSheets() {
const sApp = SpreadsheetApp.getActiveSpreadsheet();
const months = ['January','February','March','April','May','June','July',
'August','September','October','November','December'];
const master = sApp.getSheetByName('Master');
const sourceData = [];
months.forEach(m=>{
let sh = sApp.getSheetByName(m);
let vals = sh.getRange(1,2,sh.getLastRow(),27).getValues().filter(r=>r[0]!='');
sourceData.push(...vals);
});
master.getRange(1,1,sourceData.length,sourceData[0].length).setValues(sourceData);
}
Note: In my example, I removed the Other Data columns from your example to make the sheet cleaner. I also added Person column.
Try this:
Sheet1:
Code:
function filternsort() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet2');
var sheet3 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet3');
//get sheet1 data
var val = sheet.getDataRange().getValues();
var completed = [];
var notcompleted = [];
//start to 1 to exclude header
for (var i = 1; i < val.length; i++) {
if(val[i][5].toString().match(/Completed.+/)){
//add to array completed if it matches the word Completed in the Date Due column
completed.push(val[i]);
}else{
//add to array notcompleted if not match.
notcompleted.push(val[i]);
}
}
//insert header to the beginning of the array
completed.unshift(val[0]);
//set filtered values
sheet2.getRange(1, 1, completed.length, completed[0].length).setValues(completed);
//sort by Person
sheet2.getRange(2, 1, completed.length, completed[0].length).sort(1);
//insert header to the beginning of the array
notcompleted.unshift(val[0]);
//set filtered values
sheet3.getRange(1, 1, notcompleted.length, notcompleted[0].length).setValues(notcompleted);
//sort by Person
sheet3.getRange(2, 1, notcompleted.length, notcompleted[0].length).sort(1);
}
Sheet2:
Sheet3:
References:
Array.prototype.unshift()
Range.sort()
String.prototype.match()

Making a for-loop look through rows

I'm trying to make a script that replaces information in a template document (a contract). I've had it working looking through the columns of a google spreadsheet, but when trying to rewrite it to make it look through the rows of the spreadsheet instead of the columns it does not seem to work. It only returns information contained in the first row. I suspect it might have to do with the for-loop.
Here's a snippet of the code:
function lagkontrakt() {
const docFile = DriveApp.getFileById("DOC_FILE_ID");
const tempFolder = DriveApp.getFolderById("TEMP_FOLDER_ID");
const pdfFolder = DriveApp.getFolderById("PDF_FOLDER_ID");
var date = Utilities.formatDate(new Date(), "GMT+1", "dd/MM/yyyy");
//copies the template//
let copyFile = DriveApp.getFileById("FILE_ID").makeCopy(tempFolder);
let copyId = copyFile.getId();
let copyDoc = DocumentApp.openById(copyId);
//fetches the content of the template//
let copyBody = copyDoc.getBody();
let copyHeader = copyDoc.getHeader();
//defines active sheet//
let activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SHEETNAME");
let numOfRow = activeSheet.getLastRow();
//getRange(row, column, numRows, numColumns)//
let activeCol = activeSheet.getRange(1,2,numOfRow,1).getValues();
let headerCol = activeSheet.getRange(1,1,numOfRow,1).getValues();
let rowIndex = 0
//search loop//
for (; rowIndex < headerCol[0].length; rowIndex++){
copyBody.replaceText('{' + headerCol[rowIndex][0] + '}', activeCol[rowIndex][0]);
copyBody.replaceText("{dato}", date);
copyHeader.replaceText('{' + headerCol[rowIndex][0] + '}', activeCol[rowIndex][0]);
}
copyDoc.saveAndClose();
}
This is the column-version of the same script, that works just fine:
let numOfCol = activeSheet.getLastColumn();
let activeRow = activeSheet.getRange(2,1,1,numOfCol).getValues();
let headerRow = activeSheet.getRange(1,1,1,numOfCol).getValues();
let columnIndex = 0
for (; columnIndex < headerRow[0].length; columnIndex++){
copyBody.replaceText('{' + headerRow[0][columnIndex] + '}', activeRow[0][columnIndex]);
copyBody.replaceText('{dato}', date);
copyHeader.replaceText('{' + headerRow[0][columnIndex] + '}', activeRow[0][columnIndex]);
}
It looks like you have the variable in column A and the value in column B. Your For loop currently is limiting the rows that it searches to the length of the first row, which is to say that it will always be less than 1.
You need to change:
for (; rowIndex < headerCol[0].length; rowIndex++){
To:
for (; rowIndex < headerCol.length; rowIndex++){
If I'm not correct about how your data is laid out then you have reversed the array reference in a couple places and got an entire column instead of the entire row.

For loop to check values from two spreadsheets

I have two spreadsheets:
Column A on sheet 6th&7thRoster lists all IDs in a sample, contains 853 items.
Column C on sheet alreadySubmitted contains the IDs of users who've completed a task. Contains 632 items.
I'm trying to parse through both columns. If a user from Column A of sheet 6th&7thRoster matches a user from Column C of sheet sandboxAlreadySubmitted, I want to write the word "Yes" on Column I of the current row of sheet 6th&7thRoster. When using the code below, I'm not seeing not seeing any instances of the word "Yes" on Column I of 6th&7thRoster, even though I know there's multiple places where that should be the case.
function checkRoster() {
var mainSheet = SpreadsheetApp.openById('XXXXXXX');
var roster = mainSheet.getSheetByName('6th&7thRoster');
var submissions = mainSheet.getSheetByName('alreadySubmitted');
var rosterLastRow = roster.getLastRow();
var submissionsLastRow = submissions.getLastRow();
var rosterArray = roster.getRange('A2:A853').getValues();
var submissionsArray = submissions.getRange('C2:C632').getValues;
var i;
var x;
for (i = 1; i < 853; i++) {
for (x = 1; x < 632; x++){
if (rosterArray[i] == submissionsArray[x]){
roster.getRange(i, 9).setValue("Yes");
}
}
}
}
Feedback on how to solve and achieve this task will be much appreciated. For confidentiality, I cannot share the original sheets.
You want to compate the values of A2:A853 of 6th&7thRoster and C2:C632 of alreadySubmitted.
When the values of C2:C632 of alreadySubmitted are the same with the values of A2:A853 of 6th&7thRoster, you want to put Yes to the column "I".
If my understanding is correct, how about this modification? Please think of this as just one of several possible answers.
Modified script:
function checkRoster() {
var mainSheet = SpreadsheetApp.openById('XXXXXXX');
var roster = mainSheet.getSheetByName('6th&7thRoster');
var submissions = mainSheet.getSheetByName('alreadySubmitted');
var rosterLastRow = roster.getLastRow();
var submissionsLastRow = submissions.getLastRow();
var rosterArray = roster.getRange('A2:A853').getValues();
var submissionsArray = submissions.getRange('C2:C632').getValues(); // Modified
// I modified below script.
var obj = submissionsArray.reduce(function(o, [v]) {
if (v) o[v] = true;
return o;
}, {});
var values = rosterArray.map(function([v]) {return [obj[v] ? "Yes" : ""]});
roster.getRange(2, 9, values.length, values[0].length).setValues(values);
}
Flow:
Retrieve values from A2:A853 of 6th&7thRoster and C2:C632 of alreadySubmitted.
Create an object for searching the values from the values of alreadySubmitted.
Create the row values for putting to 6th&7thRoster.
References:
reduce()
map()
If I misunderstood your question and this was not the direction you want, I apologize.

C# Convert a DataTable to List<string>

I have a 1 row DataTable that I'd like to convert to the following format:
Column1Name : value
Column2Name : value
Column3Name : value
etc...
How can this be accomplished with LINQ??
Thanks!
How about something like:
DataTable table = ...
// Overlays the columns over the only row's items
// and combines each column-item pair as required.
var items = table.Columns
.Cast<DataColumn>()
.Zip(table.AsEnumerable().Single().ItemArray,
(column, value) => column.ColumnName + " : " + value);
var result = string.Join(Environment.NewLine, items);
Here's another (IMO better) approach:
// Uses the DataRow's column-indexer to match a column with
// the corresponding row-item.
var items = from DataColumn column in table.Columns
select column.ColumnName + " : " + table.Rows[0][column];
var result = string.Join(Environment.NewLine, items);

LINQ: concatenate multiple int properties into a string

I have an object with two different integer properties in it, and I'm trying to get a a new object in Linq to Entities, combining two integer properties from the same object as concatenated strings, as follows
List<DateRange> collection = (from d in context.dates
select new DateRange
{
DateString = from s in context.Seasons
where s.SeasonID = d.DateID
select string.Format("{0} - {1}", s.StartYear, s.EndYear) }
).ToList<DateRange>();
The string concatenation of the years will not compile.
This will work in LINQ to Objects, provided that each object in objects is a class or struct containing "Number1" and "Number2" fields or properties:
var results = from o in objects
select string.Format("{0} - {1}", o.Number1, o.Number2);
(However, your original should work, as well....)
Assuming you are connecting to a database via LINQ to SQL/Entities, then the String.Format call will likely fail, as with those providers, the select clause is executed within the database. Not everything can be translated from C# into SQL.
To convert your database results into a string like you want to, the following should work:
var temp = (
from d in context.dates
from s in context.Seasons
where s.SeasonID == d.DateID
select new { s.StartYear, s.EndYear }
).ToList(); // Execute query against database now, before converting date parts to a string
var temp2 =
from t in temp
select new DateRange
{
DateString = t.StartYear + " - " + t.EndYear
};
List<DateRange> collection = temp2.ToList();
EDIT:
I had an additional thought. The String.Format call is most likely the problem. I am not sure if it would work or not, but what about a plain-jane concat:
List<DateRange> collection =
(from d in context.dates
select new DateRange
{
DateString = from s in context.Seasons
where s.SeasonID = d.DateID
select s.StartYear + " - " + s.EndYear
}
).ToList<DateRange>();
Your original code works if you really want what you wrote. However, if your really want to get from
var objects = new MyObject[]{
new MyObject {Int1 = 1, Int2 = 2},
new MyObject {Int1 = 3, Int2 = 4}};
something like 1 - 2 - 3 - 4 you can write
var strings = objects.Select(o = > string.Format("{0} - {1}", o.Int1, o.Int2).ToArray();
var output = string.Join(" - ", strings);
using System.Data.Objects.SqlClient;
:
:
List<DateRange> collection = (from d in context.dates
select new DateRange
{
DateString = from s in context.Seasons
where s.SeasonID = d.DateID
select SqlFunctions.StringConvert((double)s.StartYear) + " - " +
SqlFunctions.StringConvert((double)s.EndYear)
}).ToList<DateRange>();
The StringConvert method gets converted into the proper conversion function when the LINQ statement is converted to SQL for execution on the server.

Resources