I try to place results of below link in a paged list:
LINQ to SQL and a running total on ordered results
I change the code slightly :
var userDocs = db.Docs.AsQueryable();
Int64 running_total = 0;
var moeen = userDocs.ToList()
.OrderBy(a => a.DocDate)
.Select(a =>
{
running_total += 1;
return new MoeenViewModel
{
Doc = a,
remain = running_total
};
}
);
return View(moeen.ToPagedList(1, 15));
but 'remain' results don't start with 1. it start with 8 for example if 'Docs' records count is 7
Why ??? !!!
if I return View(moeen) results is Ok
I find problem
var userDocs = db.Docs.AsQueryable();
Int64 running_total = 0;
var moeen = userDocs.ToList()
.OrderBy(a => a.DocDate)
.Select(a =>
{
running_total += 1;
return new MoeenViewModel
{
Doc = a,
remain = running_total
};
}
);
moeen = moeen.ToList();
return View(moeen.ToPagedList(1, 15));
before list moeen by .ToList(), every use of moeen cause to side effect on running_total.
See this :
http://www.blackwasp.co.uk/LinqRunningTotal.aspx
if you change the value of the variable that has been closed over before executing the query, the results will be affected.
Related
I want to delete all rows which have value of 1 in column "status_pesanan" before running create data Laravel
my controller
public function create()
{
$penjualan = DB::table('penjualan')
->groupBy('status_pesanan')
->havingRaw('COUNT(status_pesanan) = 1')
->delete();
$penjualan = new Penjualan();
$penjualan->nama_pemesan = 1;
$penjualan['no_nota'] = tambah_nol_didepan($penjualan->no_nota+1, 5);
$penjualan->alamat_pemesan = 1;
$penjualan->telepon_pemesan = 1;
$penjualan->acc_desain = 1;
$penjualan->total_item = 0;
$penjualan->total_harga = 0;
$penjualan->diskon = 0;
$penjualan->bayar = 0;
$penjualan->diterima = 0;
$penjualan->id_user = auth()->id();
$penjualan->save();
session(['id_penjualan' => $penjualan->id_penjualan]);
return redirect()->route('transaksi-baru.index');
}
If I run the code as above, the output I receive is that all old rows in the database are deleted
You are grouping by status_penasan having its count equal to 1 and call delete upon it. You wanted to remove the records whose status_penasan value is 1. This could be a fix:
$penjualan = DB::table('penjualan')
->where('status_pesanan', 1)
->delete();
I've got a for loop in App script that is looking only at rows that have data in two columns. I'd like to set a status on each row that is actually processed, but the statuses get added to the wrong rows. When I add to i it adds to the whole length of the array, so I guess I shouldn't be trying to process each row, what am I doing wrong?
function auditReport() {
var sheetname = "Sheet1"; // name of data sheet ex. Form Responses 1
var colstoworkon = 10; // how many cols are filled with data f.e. by a form
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName(sheetname));
var sheet = ss.getSheetByName(sheetname);
var data = sheet.getRange(3,1,sheet.getLastRow()-1,colstoworkon).getValues(); // starting with row 2 and column 1 as our upper-left most column,
//This makes it loops continuously and checks all not done rows
for (var i in data) {
if(data[i][1] && data[i][2]){//if email or copy are undefined just skip
var setStatus = sheet.getRange(i,4).setValue("done")
} // end of if
} // End of Loop
} //End of email function
Modification points:
In your script, from getRange(3,1,sheet.getLastRow()-1,colstoworkon), in this case, it is required to be getRange(3,1,sheet.getLastRow()-2,colstoworkon).
In the case of for (var i in data) {, i is the string type.
When you want to use sheet.getRange(i,4).setValue("done"), it is required to be sheet.getRange(Number(i) + 3, 4).setValue("done").
I thought that this might be the reason of your issue of but the statuses get added to the wrong rows..
In the case of if (data[i][1] && data[i][2]) {, if the value is 0, data[i][1] && data[i][2] is false.
When these points are reflected to your script, it becomes as follows.
Modified script:
function auditReport() {
var sheetname = "Sheet1";
var colstoworkon = 10;
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName(sheetname));
var sheet = ss.getSheetByName(sheetname);
var data = sheet.getRange(3, 1, sheet.getLastRow() - 2, colstoworkon).getDisplayValues();
for (var i in data) {
if (data[i][1] && data[i][2]) {
var setStatus = sheet.getRange(Number(i) + 3, 4).setValue("done");
}
}
}
Or, your script can be also modified as follows. In this modification, done is put using the range list. By this, the process cost can be reduced a little.
function auditReport() {
var sheetname = "Sheet1";
var colstoworkon = 10;
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName(sheetname));
var sheet = ss.getSheetByName(sheetname);
var data = sheet.getRange(3, 1, sheet.getLastRow() - 2, colstoworkon).getDisplayValues();
var ranges = data.map(([,b,c], i) => b && c ? `D${i + 3}` : "").filter(String);
if (ranges.length == 0) return;
sheet.getRangeList(ranges).setValue("done");
}
References:
for...in
getRangeList(a1Notations)
I am having a problem where I have a 12 month period but beginning from zero index and its causing me to have an invalid date. Below is a picture of both my date array and chart js. Basically, because the months are zero indexed, they are out by one
and the below is the Linq query I am using, if anyone can help me fix this that would be great
var solicitor = _db.Records
.Where(j => j.Requestor == "Solicitor" && EF.Functions.DateDiffMonth(j.Request_Date, DateTime.Now) == 0 && EF.Functions.DateDiffMonth(j.Request_Date, DateTime.Now) <= 12)
.GroupBy(g => new { g.Request_Date.Value.Year, g.Request_Date.Value.Month }).OrderBy(d => d.Key.Year).ThenBy(d => d.Key.Month)
.Select(group => new
{
Dates = group.Key,
Count = group.Count()
});
var solicitorCount = solicitor.Select(n => n.Count).ToArray();
var date = solicitor.Select(n => n.Dates).ToArray();
and the code inside my chart js to format as moment
let newArr = []
for (let i = 0; i < simpleData[0].date.length; i++) {
var calDate = moment(simpleData[0].date[i]).format('MMM YYYY');
console.log(calDate)
newArr.push(calDate)
}
You can change your code like this:
let newArr = []
for (let i = 0; i < simpleData[0].date.length; i++) {
data[i].month -= 1;
var calDate = moment(simpleData[0].date[i]).format('MMM YYYY');
console.log(calDate)
newArr.push(calDate)
}
newArr result:
GAS is quite powerful and you could write a full fledged web-app using a Google Sheet as the DB back-end. There are many reasons not to do this but I figure in some cases it is okay.
I think the biggest issue will be performance issues when looking for rows based on some criteria in a sheet with a lot of rows. I know there are many ways to "query" a sheet but I can't find reliable information on which is the fastest.
One of the complexities is that many people can edit a sheet which means there are a variable number of situations you'd have to account for. For the sake of simplicity, I want to assume the sheet:
Is locked down so only one person can see it
The first column has the row number (=row())
The most basic query is finding a row where a specific column equals some value.
Which method would be the fastest?
I have a sheet with ~19k rows and ~38 columns, filled with all sorts of unsorted real-world data. That is almost 700k rows so I figured it would be a good sheet to time a few methods and see which is the fastest.
method 1: get sheet as a 2D array then go through each row
method 2: get sheet as a 2D array, sort it, then using a binary search algorithm to find the row
method 3: make a UrlFetch call to Google visualization query and don't provide last row
method 4: make a UrlFetch call to Google visualization query and provide last row
Here are the my query functions.
function method1(spreadsheetID, sheetName, columnIndex, query)
{
// get the sheet values excluding header,
var rowValues = SpreadsheetApp.openById(spreadsheetID).getSheetByName(sheetName).getSheetValues(2, 1, -1, -1);
// loop through each row
for(var i = 0, numRows = rowValues.length; i < numRows; ++i)
{
// return it if found
if(rowValues[i][columnIndex] == query) return rowValues[i]
}
return false;
}
function method2(spreadsheetID, sheetName, columnIndex, query)
{
// get the sheet values excluding header
var rowValues = SpreadsheetApp.openById(spreadsheetID).getSheetByName(sheetName).getSheetValues(2, 1, -1, -1);
// sort it
rowValues.sort(function(a, b){
if(a[columnIndex] < b[columnIndex]) return -1;
if(a[columnIndex] > b[columnIndex]) return 1;
return 0;
});
// search using binary search
var foundRow = matrixBinarySearch(rowValues, columnIndex, query, 0, rowValues.length - 1);
// return if found
if(foundRow != -1)
{
return rowValues[foundRow];
}
return false;
}
function method3(spreadsheetID, sheetName, queryColumnLetterStart, queryColumnLetterEnd, queryColumnLetterSearch, query)
{
// SQL like query
myQuery = "SELECT * WHERE " + queryColumnLetterSearch + " = '" + query + "'";
// the query URL
// don't provide last row in range selection
var qvizURL = 'https://docs.google.com/spreadsheets/d/' + spreadsheetID + '/gviz/tq?tqx=out:json&headers=1&sheet=' + sheetName + '&range=' + queryColumnLetterStart + ":" + queryColumnLetterEnd + '&tq=' + encodeURIComponent(myQuery);
// fetch the data
var ret = UrlFetchApp.fetch(qvizURL, {headers: {Authorization: 'Bearer ' + ScriptApp.getOAuthToken()}}).getContentText();
// remove some crap from the return string
return JSON.parse(ret.replace("/*O_o*/", "").replace("google.visualization.Query.setResponse(", "").slice(0, -2));
}
function method4(spreadsheetID, sheetName, queryColumnLetterStart, queryColumnLetterEnd, queryColumnLetterSearch, query)
{
// find the last row in the sheet
var lastRow = SpreadsheetApp.openById(spreadsheetID).getSheetByName(sheetName).getLastRow();
// SQL like query
myQuery = "SELECT * WHERE " + queryColumnLetterSearch + " = '" + query + "'";
// the query URL
var qvizURL = 'https://docs.google.com/spreadsheets/d/' + spreadsheetID + '/gviz/tq?tqx=out:json&headers=1&sheet=' + sheetName + '&range=' + queryColumnLetterStart + "1:" + queryColumnLetterEnd + lastRow + '&tq=' + encodeURIComponent(myQuery);
// fetch the data
var ret = UrlFetchApp.fetch(qvizURL, {headers: {Authorization: 'Bearer ' + ScriptApp.getOAuthToken()}}).getContentText();
// remove some crap from the return string
return JSON.parse(ret.replace("/*O_o*/", "").replace("google.visualization.Query.setResponse(", "").slice(0, -2));
}
My binary search algorithm:
function matrixBinarySearch(matrix, columnIndex, query, firstIndex, lastIndex)
{
// find the value using binary search
// https://www.w3resource.com/javascript-exercises/javascript-array-exercise-18.php
// first make sure the query string is valid
// if it is less than the smallest value
// or larger than the largest value
// it is not valid
if(query < matrix[firstIndex][columnIndex] || query > matrix[lastIndex][columnIndex]) return -1;
// if its the first row
if(query == matrix[firstIndex][columnIndex]) return firstIndex;
// if its the last row
if(query == matrix[lastIndex][columnIndex]) return lastIndex;
// now start doing binary search
var middleIndex = Math.floor((lastIndex + firstIndex)/2);
while(matrix[middleIndex][columnIndex] != query && firstIndex < lastIndex)
{
if(query < matrix[middleIndex][columnIndex])
{
lastIndex = middleIndex - 1;
}
else if(query > matrix[middleIndex][columnIndex])
{
firstIndex = middleIndex + 1;
}
middleIndex = Math.floor((lastIndex + firstIndex)/2);
}
return matrix[middleIndex][columnIndex] == query ? middleIndex : -1;
}
This is the function I used to test them all:
// each time this function is called it will try one method
// the first time it is called it will try method1
// then method2, then method3, then method4
// after it does method4 it will start back at method1
// we will use script properties to save which method is next
// we also want to use the same query string for each batch so we'll save that in script properties too
function testIt()
{
// get the sheet where we're staving run times
var runTimesSheet = SpreadsheetApp.openById("...").getSheetByName("times");
// we want to see true speed tests and don't want server side caching so we a copy of our data sheet
// make a copy of our data sheet and get its ID
var tempSheetID = SpreadsheetApp.openById("...").copy("temp sheet").getId();
// get script properties
var scriptProperties = PropertiesService.getScriptProperties();
// the counter
var searchCounter = Number(scriptProperties.getProperty("searchCounter"));
// index of search list we want to query for
var searchListIndex = Number(scriptProperties.getProperty("searchListIndex"));
// if we're at 0 then we need to get the index of the query string
if(searchCounter == 0)
{
searchListIndex = Math.floor(Math.random() * searchList.length);
scriptProperties.setProperty("searchListIndex", searchListIndex);
}
// query string
var query = searchList[searchListIndex];
// save relevant data
var timerRow = ["method" + (searchCounter + 1), searchListIndex, query, 0, "", "", "", ""];
// run the appropriate method
switch(searchCounter)
{
case 0:
// start time
var start = (new Date()).getTime();
// run the query
var ret = method1(tempSheetID, "Extract", 1, query);
// end time
timerRow[3] = ((new Date()).getTime() - start) / 1000;
// if we found the row save its values in the timer output so we can confirm it was found
if(ret)
{
timerRow[4] = ret[0];
timerRow[5] = ret[1];
timerRow[6] = ret[2];
timerRow[7] = ret[3];
}
break;
case 1:
var start = (new Date()).getTime();
var ret = method2(tempSheetID, "Extract", 1, query);
timerRow[3] = ((new Date()).getTime() - start) / 1000;
if(ret)
{
timerRow[4] = ret[0];
timerRow[5] = ret[1];
timerRow[6] = ret[2];
timerRow[7] = ret[3];
}
break;
case 2:
var start = (new Date()).getTime();
var ret = method3(tempSheetID, "Extract", "A", "AL", "B", query);
timerRow[3] = ((new Date()).getTime() - start) / 1000;
if(ret.table.rows.length)
{
timerRow[4] = ret.table.rows[0].c[0].v;
timerRow[5] = ret.table.rows[0].c[1].v;
timerRow[6] = ret.table.rows[0].c[2].v;
timerRow[7] = ret.table.rows[0].c[3].v;
}
break;
case 3:
var start = (new Date()).getTime();
var ret = method3(tempSheetID, "Extract", "A", "AL", "B", query);
timerRow[3] = ((new Date()).getTime() - start) / 1000;
if(ret.table.rows.length)
{
timerRow[4] = ret.table.rows[0].c[0].v;
timerRow[5] = ret.table.rows[0].c[1].v;
timerRow[6] = ret.table.rows[0].c[2].v;
timerRow[7] = ret.table.rows[0].c[3].v;
}
break;
}
// delete the temp file
DriveApp.getFileById(tempSheetID).setTrashed(true);
// save run times
runTimesSheet.appendRow(timerRow);
// start back at 0 if we're the end
if(++searchCounter == 4) searchCounter = 0;
// save the search counter
scriptProperties.setProperty("searchCounter", searchCounter);
}
I have a global variable searchList that is an array of various query strings -- some are in the sheet, some are not.
I ran testit on a trigger to run every minute. After 152 iterations I had 38 batches. Looking at the result, this is what I see for each method:
| Method | Minimum Seconds | Maximum Seconds | Average Seconds |
|---------|-----------------|-----------------|-----------------|
| method1 | 8.24 | 36.94 | 11.86 |
| method2 | 9.93 | 23.38 | 14.09 |
| method3 | 1.92 | 5.48 | 3.06 |
| method4 | 2.20 | 11.14 | 3.36 |
So it appears that, at least for my data-set, is using Google visualization query is the fastest.
I want to do the following
var totalNoOfRows = result.First().TotalNumberOfCount;
And finally do something like that
bookssList.AddRange(retResult.Select(r => r.ToBook()));
where ToBook is extended method
but I always get The result of a query cannot be enumerated more than once.
if (result != null)
{
var totalNoOfRows = result.First().TotalNumberOfCount;
pagingContext.ItemsTotal = totalNoOfRows != null ? int.Parse(totalNoOfRows.ToString()) : 0;
var retResult = result.ToList();
// pagingContext.ItemsTotal = totalcount.Value != null ? int.Parse(totalcount.Value.ToString()) : 0;
bookssList.AddRange(retResult.Select(r => r.ToBook()));
}
Hard to guess what are you doing, and how these snippets relate to each other, but if you can enumerate a collection only once, then call ToArray first:
var resultCopy = result.ToArray();
//... any number of operations on resultCopy
Note that calling First also counts as enumerating. So you need to enumerate and copy the collection even before this.
Try changing the code to this, so you only enumerate result once:
var retResult = result.ToList();
var totalNoOfRows = retResult.First().TotalNumberOfCount; //You are now using LINQ on the list, not the query!
pagingContext.ItemsTotal = totalNoOfRows != null ? int.Parse(totalNoOfRows.ToString()) : 0;
// pagingContext.ItemsTotal = totalcount.Value != null ? int.Parse(totalcount.Value.ToString()) : 0;
Logger.LogInfo("Search Payments GetPaymentsWithCount stored procedure result not null and count=" + totalcount);
bookssList.AddRange(retResult.Select(r => r.ToBook()));