Is there any solution for getting next row in Sheet7 - for-loop

function Copy_data(){
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ts = sss.getSheetByName('Sheet7');
var nextrow = ts.getLastRow()+1;
var ss = sss.getSheetByName('Sheet6');
var lastRow= ss.getLastRow();
for(var i = 2; i<lastRow;i++){
var Date = ss.getRange(i,1).getValues();
var Product = ss.getRange(i,2).getValues();
var URL = ss.getRange(i,7).getValues();
if(URL = ss.getRange(i,7).getRichTextValue().getLinkUrl()){
for (var j = 2; j<nextrow;j++){
ts.getRange(j,1).setValues(Date);
ts.getRange(j,2).setValues(Product);
}}}}
My i loop is incrementing but j is not getting next row. i is putting value in row 2 of the Sheet7. It is just replacing value in second row. I did not getting result in the next row.

Related

Google Apps Script - Copy Paste values on same page and in same row based on cell criteria

I have a table with data in columns A:Ak. Some of the data contains formulas and when a row of data is complete (i.e. the status in column W is "Y") I would like to copy that row in place and only past the values.
This is what I have tried:
function Optimize() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
// This searches all cells in columns W copies and pastes values in row if cell has value 'Y'
if (row[22] == 'Y') {
sheet.getDataRange(sheet.getCurrentCell().getRow() - 0, 1, 1, sheet.getMaxColumns()).activate();
sheet.getActiveRange().copyTo(sheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
rowsCopiedpasted++;
}
}
}
When I am in the sheet and click on the cell in column A of a row with a "Y" value this works, but I need it to go through the whole sheet and copy/paste values of all rows with "Y" in column W.
Try this:
function Optimize() {
const sh = SpreadsheetApp.getActiveSheet();
const rg = sh.getDataRange();
const vs = rg.getValues();
const lc = sh.getLastColumn();
let o = vs.forEach((r,i) => {
if(r[22] == "Y") {
sh.getRange(i + 1, lc + 1,1,lc).setValues([r])
}
});
}

Simple nested for loop problems

I am very new to programming and I am trying to create a short macro on google sheets. I want to copy and paste a list of cells a certain number of times based on variables see here. On the first column you have a list of locations and the second column the number of times they should be pasted into a 3rd column (column F). For example i would like to paste A4 fives times into column F4. Then A5 twice into F8 (F4 + 5 rows) and so one until the end of my list.
I came up with the below code but it currently copies each locations of my list 5 times on the same rows (F4-F8) then repeat the same process on the following 5 rows, and so on. I think the issue is with the order of the loops but i can't figure it out really.
Another problem i have is that It only copies each location 5 times (B1), but i am not sure how to make my variable numShift an array for (B4-B16) so that each locations is copied the correct number of times
Any help would be really appreciated !
function myFunction() {
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet()
var activeSheet = ss.getActiveSheet()
var numShift = activeSheet.getRange(4,2).getValue()
for (var k=0;k<65;k=k+5){
for (var indexNumLocation=0;indexNumLocation<15;indexNumLocation++){
for (var indexNumShift=0;indexNumShift<numShift;indexNumShift++)
{var locationRange = activeSheet.getRange(4+indexNumLocation,1).getValue();
activeSheet.getRange(4+k+indexNumShift,6).setValue(locationRange);
}
}
}
}
Try this
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastRow = sheet.getLastRow();
var data = [];
var i, j;
// get times count for each row from B4 to last row
var times = sheet.getRange(4, 2, lastRow - 4, 1).getValues();
// get the data that needs to be copied for each row from A4 to last row
var values = sheet.getRange(4, 1, lastRow - 4, 1).getValues();
// loop over each row
for (i = 4; i <= lastRow; i++) {
// run loop number of times for that row
for (j = 1; j <= times[i]; j++) {
// push the value that needs to be copied in a temporary array
data.push([values[i][0]]);
}
}
// finally put that array data in sheet starting from F4
sheet.getRange(4, 6, data.length, 1).setValues(data);
}

Data validation depending on condition Google Spreadsheets

In Google Spreadsheets, is it possible to make a cell data validation for a range of numbers depending on the result of a condition?
Example:
column1 column2
a if column1=a then show dropdown of (1,0) if b then (2,3) so [1,0]
a if column1=a then show dropdown of (1,0) if b then (2,3) so [1,0]
b if column1=a then show dropdown of (1,0) if b then (2,3) so [2,0]
a if column1=a then show dropdown of (1,0) if b then (2,3) so [1,0]
I am not exactly clear on what you want, but you can
have different dropdowns based on different values in
a cell. This code assumes the value to evaluate is in
cell A1 ('a', 'b', or another value). If A1=a then the
dropdown list comes from C1:C2. If A1=b then the dropdown
list is from D1:D2. Any other value in A1 removes the
valadation in B1. (Entering a or b in A1 will put the
valadation back).
function onEdit(e)
{
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var sheetName= sheet.getSheetName();
if(sheetName=="Sheet1"){ //Only Sheet1
var editRange = sheet.getActiveRange();
var editRow = editRange.getRow();
var editCol = editRange.getColumn();
var lr = sheet.getLastRow()
var range = sheet.getRange("A1"); // Only apply on edit to cell Sheet1!A1
var rangeRowStart = range.getRow();
var rangeRowEnd = rangeRowStart + range.getHeight()-1;
var rangeColStart = range.getColumn();
var rangeColEnd = rangeColStart + range.getWidth()-1;
if (editRow >= rangeRowStart && editRow <= rangeRowEnd
&& editCol >= rangeColStart && editCol <= rangeColEnd)
{
drop()
}}}
function drop(){
var cell = SpreadsheetApp.getActive().getRange('B1');//set validation in B1
var ocell=SpreadsheetApp.getActive().getRange('B1').offset(0, -1).getValue()//evaluate value in A1
var cellVal=cell.getValue()
if(ocell=="a"){ //If A1=a dropdown list is C1:C2
var range = SpreadsheetApp.getActive().getRange('C1:C2');
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();
cell.setDataValidation(rule)}
else if(ocell=="b"){ //if A1=b dropdown list = D1:D2
var range = SpreadsheetApp.getActive().getRange('D1:D2');
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();
cell.setDataValidation(rule);}
else{ // If A1 is not a or b remove validation from B1
cell.clearDataValidations()}
}
If this is not what you are looking for explain and I can adjust.

Linq FirstOrDefault List inside a query

I have this linq query
var numberGroups =
from n in VISRUBs.Where(a => a.VISANA.VISITE.DATEVIS <= d && a.VISANA.VISITE.PANUM == p)
group n by n.RUBRIQUE into g
select new {
RemainderCHAPLIB = g.Key.ANALYSE.CHAPITRE.LIBELLE,
RemainderLIB = g.Key.LIBELLE,
RemainderRUNUM = g.Key.RUNUM,
vals = from vlist in g.OrderByDescending(a=>a.VISANA.VISITE.DATEVIS)
select vlist.VALEUR
};
which gives me this result in Linqpad
What I want is to select the first and second item from the last field (vals) which is a List<string>.
I have tried this:
var numberGroups =
from n in VISRUBs.Where(a => a.VISANA.VISITE.DATEVIS <= d && a.VISANA.VISITE.PANUM == p)
group n by n.RUBRIQUE into g
select new {
RemainderCHAPLIB = g.Key.ANALYSE.CHAPITRE.LIBELLE,
RemainderLIB = g.Key.LIBELLE,
RemainderRUNUM = g.Key.RUNUM,
vals = from vlist in g.OrderByDescending(a => a.VISANA.VISITE.DATEVIS)
select vlist.VALEUR
};
var lst = from n in numberGroups
select new
{
RemainderCHAPLIB = n.RemainderCHAPLIB,
RemainderLIB = n.RemainderLIB,
RemainderRUNUM = n.RemainderRUNUM,
VAL = n.vals.FirstOrDefault()
};
but it didn't work, I got an exception:
Dynamic SQL ErrorSQL error code = -104Token unknown - line 54, column 1OUTER
found it !
var lst = from n in numberGroups.ToList()
select new
{
RemainderCHAPLIB = n.RemainderCHAPLIB,
RemainderLIB = n.RemainderLIB,
RemainderRUNUM = n.RemainderRUNUM,
VAL = n.vals.FirstOrDefault(),
ANT = n.vals.Skip(1).FirstOrDefault()
};

LINQ Grouping: Is there a cleaner way to do this without a for loop

I am trying to create a very simple distribution chart and I want to display the counts of tests score percentages in their corresponding 10's ranges.
I thought about just doing the grouping on the Math.Round((d.Percentage/10-0.5),0)*10 which should give me the 10's value....but I wasn't sure the best way to do this given that I would probably have missing ranges and all ranges need to appear even if the count is zero. I also thought about doing an outer join on the ranges array but since I'm fairly new to Linq so for the sake of time I opted for the code below. I would however like to know what a better way might be.
Also note: As I tend to work with larger teams with varying experience levels, I'm not all that crazy about ultra compact code unless it remains very readable to the average developer.
Any suggestions?
public IEnumerable<TestDistribution> GetDistribution()
{
var distribution = new List<TestDistribution>();
var ranges = new int[] { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110 };
var labels = new string[] { "0%'s", "10%'s", "20%'s", "30%'s", "40%'s", "50%'s", "60%'s", "70%'s", "80%'s", "90%'s", "100%'s", ">110% "};
for (var n = 0; n < ranges.Count(); n++)
{
var count = 0;
var min = ranges[n];
var max = (n == ranges.Count() - 1) ? decimal.MaxValue : ranges[n+1];
count = (from d in Results
where d.Percentage>= min
&& d.Percentage<max
select d)
.Count();
distribution.Add(new TestDistribution() { Label = labels[n], Frequency = count });
}
return distribution;
}
// ranges and labels in a list of pairs of them
var rangesWithLabels = ranges.Zip(labels, (r,l) => new {Range = r, Label = l});
// create a list of intervals (ie. 0-10, 10-20, .. 110 - max value
var rangeMinMax = ranges.Zip(ranges.Skip(1), (min, max) => new {Min = min, Max = max})
.Union(new[] {new {Min = ranges.Last(), Max = Int32.MaxValue}});
//the grouping is made by the lower bound of the interval found for some Percentage
var resultsDistribution = from c in Results
group c by
rangeMinMax.FirstOrDefault(r=> r.Min <= c.Percentage && c.Percentage < r.Max).Min into g
select new {Percentage = g.Key, Frequency = g.Count() };
// left join betweem the labels and the results with frequencies
var distributionWithLabels =
from l in rangesWithLabels
join r in resultsDistribution on l.Range equals r.Percentage
into rd
from r in rd.DefaultIfEmpty()
select new TestDistribution{
Label = l.Label,
Frequency = r != null ? r.Frequency : 0
};
distribution = distributionWithLabels.ToList();
Another solution if the ranges and labels can be created in another way
var ranges = Enumerable.Range(0, 10)
.Select(c=> new {
Min = c * 10,
Max = (c +1 )* 10,
Label = (c * 10) + "%'s"})
.Union(new[] { new {
Min = 100,
Max = Int32.MaxValue,
Label = ">110% "
}});
var resultsDistribution = from c in Results
group c by ranges.FirstOrDefault(r=> r.Min <= c.Percentage && c.Percentage < r.Max).Min
into g
select new {Percentage = g.Key, Frequency = g.Count() };
var distributionWithLabels =
from l in ranges
join r in resultsDistribution on l.Min equals r.Percentage
into rd
from r in rd.DefaultIfEmpty()
select new TestDistribution{
Label = l.Label,
Frequency = r != null ? r.Frequency : 0
};
This works
public IEnumerable<TestDistribution> GetDistribution()
{
var range = 12;
return Enumerable.Range(0, range).Select(
n => new TestDistribution
{
Label = string.Format("{1}{0}%'s", n*10, n==range-1 ? ">" : ""),
Frequency =
Results.Count(
d =>
d.Percentage >= n*10
&& d.Percentage < ((n == range - 1) ? decimal.MaxValue : (n+1)*10))
});
}

Resources