I have a 3 column excel table where I want to store a group and 2 dimentions.
Dim 1 is always just a single value
Dim 2 can be multiple intervals separated by commas or just a single value.
My aim is to generate a table with all different combinations in powerquery in order to use it as a key. Some help on this would be much appreciated!
Start:
Finish:
I ended up using this but i'm very grateful for your code :)
let
Källa = Excel.CurrentWorkbook(){[Name="Tabell1"]}[Content],
#"Ändrad typ" = Table.TransformColumnTypes(Källa,{{"Group", type text}, {"Dim1", type text}, {"Dim2", type any}}),
#"Ändrad typ1" = Table.TransformColumnTypes(#"Ändrad typ",{{"Dim2", type text}}),
#"Lägg till egen" = Table.AddColumn(#"Ändrad typ1", "Ansvar", each let
commaList = Text.Split([Dim2], ","),
totList = List.Accumulate(commaList, {}, (state, current) =>
let
intervalList = Text.Split(current,"-"),
startValue = Number.From(List.First(intervalList)),
endValue = Number.From(List.Last(intervalList)),
genList = state & List.Generate(() => startValue, each _ <= endValue, each _ + 1)
in
genList
),
transformedList = List.Transform(totList, each Number.ToText(_)),
stringFromList = Text.Combine(transformedList, ", ")
in
transformedList),
#"Borttagna kolumner" = Table.RemoveColumns(#"Lägg till egen",{"Dim2"}),
#"Expanderad Ansvar" = Table.ExpandListColumn(#"Borttagna kolumner", "Ansvar")
in
#"Expanderad Ansvar"
I used the following M-Code to transform your start table into the desired result
let
Quelle = Excel.CurrentWorkbook(){[Name="tblData"]}[Content],
#"Geänderter Typ" = Table.TransformColumnTypes(Quelle,{{"group", type text}, {"dim1", Int64.Type}, {"dim2", type text}}),
#"Hinzugefügte benutzerdefinierte Spalte" = Table.AddColumn(#"Geänderter Typ", "dim2_list", each Text.Split([dim2],",")),
#"Erweiterte Benutzerdefiniert" = Table.ExpandListColumn(#"Hinzugefügte benutzerdefinierte Spalte", "dim2_list"),
#"Entfernte Spalten" = Table.RemoveColumns(#"Erweiterte Benutzerdefiniert",{"dim2"}),
#"Hinzugefügte benutzerdefinierte Spalte1" = Table.AddColumn(#"Entfernte Spalten", "dim2", each fnCreateList([dim2_list])),
#"Erweiterte dim2" = Table.ExpandListColumn(#"Hinzugefügte benutzerdefinierte Spalte1", "dim2"),
#"Entfernte Spalten1" = Table.RemoveColumns(#"Erweiterte dim2",{"dim2_list"})
in
#"Entfernte Spalten1"
Function fnCreateList used in the above code
(inp as text) =>
let
secondNo= Number.FromText(Text.End(inp,Text.Length(inp)- Text.PositionOf(inp,"-")-1)),
firstNo =try Number.FromText(Text.Start(inp,Text.PositionOf(inp,"-"))) otherwise secondNo,
result= {firstNo .. secondNo}
in
result
Related
Our company offers soft loans to employees. I am trying to write some code that will set up a loan deduction schedule once a loan is approved. This is all done on google sheets. The schedule then can be linked to payroll etc.
The approved loans will appear in a format like this:-
Loans Approved - [Serial, Employee ID,Amount, Monthly Deductions,Requested Date,Deduction Start Date]
I am looking to build an array that will have the first 4 elements that repeat and the deduction month to increase by 1
So far this is my code
function myFunction() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("Loans");
var range = sheet.getDataRange();
var data = range.getValues()
var lastRow = range.getLastRow()
var scheduleSheet = ss.getSheetByName("Schedule")
var scheuduleLastRow = scheduleSheet.getDataRange().getLastRow;
for(let i=1;i<lastRow;i++){
var serial = data [i][0]
var id = data [i][1]
var amount = data[i][2]
var monthlyRepayment = data [i][3]
var startDate = new Date (data [i][5])
var markScheduleDone = sheet.getRange(i+1,7)
var fullMonths = Math.floor(amount/monthlyRepayment)
var remainderMonth = (amount/monthlyRepayment)-fullMonths
var remainderAmount = Math.round(remainderMonth*monthlyRepayment)
for (let j=1;j<=fullMonths+1;j++){
var incrementalMonths = new Date(startDate.setMonth(startDate.getMonth()+1)) ;
}
var newArray = [serial,id,monthlyRepayment];
var remainderArray = [serial,id,remainderAmount];
var reptArray = Array(fullMonths).fill(newArray);
var finalArray = [...reptArray,remainderArray]
Logger.log(finalArray)
var toPasteto = scheduleSheet.getRange(scheuduleLastRow+1,1,finalArray.length,3)
toPasteto.setValues(finalArray)
markScheduleDone.setValue ("Done")
}
}
I am close but I cant figure out how to join the incrementalMonths to the finalarray.
This is the first time im using a loop within a loop
Also any guidance if I could have done this better?
Kinldy requesting some guidance
I'm not sure if this is exactly what you are looking for but try this.
Notice I fill the array finalArray with all the newArrays so I only have to setValues() once. Same with markDone.
I increment the month but if the day happens to fall outside the number of days in a month it will increment to another day. So for any 28 or 30 day months there should be another check but I didn't do that.
My particular style of coding is to always use a try {} catch() {} block, always terminate a line with semicolon ;, and to use let instead of var whenever possible.
function myFunction() {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Loans");
var range = sheet.getDataRange();
var data = range.getValues()
var lastRow = range.getLastRow()
var scheduleSheet = ss.getSheetByName("Schedule")
var scheuduleLastRow = scheduleSheet.getDataRange().getLastRow();
let finalArray = [];
let markDone = [];
for(let i=1;i<lastRow;i++){
var serial = data [i][0];
var id = data [i][1];
var amount = data[i][2];
var monthlyRepayment = data [i][3];
var startDate = new Date (data [i][5]);
var fullMonths = Math.floor(amount/monthlyRepayment);
var remainderMonth = (amount/monthlyRepayment)-fullMonths;
var remainderAmount = Math.round(remainderMonth*monthlyRepayment);
let day = startDate.getDate();
if( day > 28 ) throw "This function has not adjusted for short months"
let month = startDate.getMonth();
let year = startDate.getFullYear();
let newArray = [];
for (let j=1;j<=fullMonths+1;j++){
month++;
if( month > 11 ) {
month = 0;
year++;
}
var date = new Date(year,month,day);
newArray.push([serial,id,monthlyRepayment,date])
}
newArray.push([serial,id,remainderAmount,date]);
finalArray = finalArray.concat(newArray);
Logger.log(newArray);
markDone.push(["Done"]);
}
sheet.getRange(2,7,markDone.length,1).setValues(markDone);
scheduleSheet.getRange(scheuduleLastRow+1,1,finalArray.length,4).setValues(finalArray);
}
catch(err) {
Logger.log(err)
}
}
I seek help in finalizing a MACRO script for following task.
Basic requirements is to get data from 'Ref' tab (around 200 symbols) in a sequence to 'Mcontrol'!E3 (this tab imports a table from the Website)
From this table, sorted highest 2 values of Symbol/s is extracted and exported to 'OI EOD' tab,
i.e. required value is shown in 'OI EOD'! G5:K5.
Then, copy paste range 'OI EOD'! G5:K5 , to the matched row (symbol match to 'Mcontrol'!E3) in the table below ('OI EOD'!C6:C210).
I seek help in putting forEach condition in the macro. SO that next cycle of copy paste function is completed, for each new value in Cell_1 reference.
Hope ppl take notice of the help request from a novice and give good solution and guidance.
Sheet link is Following
https://docs.google.com/spreadsheets/d/1az2kas91KFxHcWhtWDg-g20hNYR-_zEiWPvYs-x42YU/edit?usp=sharing
Macro -
var wb = SpreadsheetApp.getActive();
var sh1= wb.getSheetByName('Mcontrol');
var sh2= wb.getSheetByName('OI EOD');
var R1 = 3
var C1 = 3
var R2 = 3
var C2 = 2
var R3 = 3
var C3 = 5
var cell_1 = sh1.getRange(R1,C1).getValue();
var cell_2 = sh1.getRange(R2,C2).getValue();
var cell_3 = sh1.getRange(R3,C3).getValue();
for(i=1;i<cell_2;i++);
cell_1 = i + 1;
sh1.getRange(R1,C1).setValue(cell_1);{
var Range_1 = sh2.getRange(6, 3, 300);
var row_i = 5 ; // source row no. for data copy paste
const Row_1 = row_i;
const Col_2 = 7;
const Row_Offet1 = 1;
const Col_Offet1 = 5;
var data = Range_1.getValues();
let ABC = cell_3;
let row = 1 + data.findIndex(users => {return users[0] == ABC});
var row_target = row + row_i ;
const sourceRange = sh2.getRange(Row_1,Col_2,Row_Offet1,Col_Offet1);
var destRange = sh2.getRange(row_target,Col_2,Row_Offet1,Col_Offet1);
sourceRange.copyTo(destRange,SpreadsheetApp.CopyPasteType.PASTE_VALUES,false);
for(k=1;k<cell_1.i;k++);
Logger.log(row_target);
Logger.log(k) ;
}
//NEXT
// cell_1=sh1.getRange(R1, C1);
// sh1.getRange(R1,C1).setValue(i);
}
}```
Thanks
Robin
Not sure if understand the task as a whole, so here is a guess.
This way you can get values from some start to some end 'symbols' (or numbers) and put these values in correct rows on the 'OI EOD' sheet:
function main() {
var start = 1;
var end = 5;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mcontrol_sheet = ss.getSheetByName('Mcontrol');
var oi_eod_sheet = ss.getSheetByName('OI EOD');
// get a list of all Symbols from column 'C' of the sheet 'OI EOD'
var symbols = oi_eod_sheet.getRange('c6:c').getValues().flat();
for (let i = start; i <= end; i++) {
// set the number i on the sheet 'Mcontrol' into the cell 'C3'
mcontrol_sheet.getRange('c3').setValue(i);
// wait to complete all the changes on the spreadsheet
SpreadsheetApp.flush();
// get the Symbol and Values from the range 'C5:K5' of the sheet 'OI EOD'
var [symbl, _, _, _, ...values] = oi_eod_sheet.getRange('c5:k5').getValues()[0];
// get the row index for this Symbol on the sheet 'Mcontrol'
var row = symbols.indexOf(symbl) + 6;
console.log({row}, {symbl}, {values});
// set the values on the sheet 'OI EOD' on the row with given index
oi_eod_sheet.getRange('g' + row + ':k' + row).setValues([values]);
}
}
It's up to you to decide how the start and end values could be defined. It can be values from two cells. Or it can be selected range. Or something else.
I have created an array to store some excel cell addresses, but while running through a portion of the array with a For Loop (because the values are in order for that portion of the data and its easier than doing each one at a time), but I get an error at line 22,(saData(i,1) = "D" count), right at the "count" variable but I cant figure out why.
'LOADING EXCEL CELL ADDRESS INTO INPUT ARRAY
saData(0,1) = "C3"
saData(1,1) = "F3"
saData(2,1) = "C4"
saData(3,1) = "F4"
saData(4,1) = "F6"
saData(5,1) = "C7"
saData(6,1) = "F7"
saData(7,1) = "C8"
saData(8,1) = "C9"
saData(9,1) = "F9"
saData(10,1) = "C11"
saData(12,1) = "F11"
saData(13,1) = "C13"
saData(14,1) = "F13"
Dim count : count = 16
For i = 15 to 54
saData(i,1) = "D" count
count = count + 1
next'
saData(55,1) = "G59"
saData(56,1) = "F60"
saData(57,1) = "B64"
saData(58,1) = "E64"
For i = 0 to 59
Msgbox saData(i,1)
next
Just try concatenating saData(i,1) = "D"&count
I am getting this error when I try to join
var users = _users.Get();
var userApprovals =
(from approval in _entities.ApprovalEntities
join userDetail in users on approval.UserKey equals userDetail.UserId
where approval.EmployeeUid == employeeUid
select new UserApproval
{
Id = approval.Id,
EmployeeUid = approval.EmployeeUid,
UserKey = approval.UserKey,
UserId = approval.UserId,
UserName = userDetail.FirstName + " " + userDetail.LastName
}).ToList();
error
Only primitive types or enumeration types are supported in this context
Thanks
fixed,
var userApprovals =
(from approval in _entities.ApprovalEntities.ToList()
join userDetail in users on approval.UserKey equals userDetail.UserId
where approval.EmployeeUid == employeeUid
select new UserApproval
{
Id = approval.Id,
EmployeeUid = approval.EmployeeUid,
UserKey = approval.UserKey,
UserId = approval.UserId,
UserName = userDetail.FirstName + " " + userDetail.LastName
}).ToList();
In my case, my problem was use IEnumerable without using toList() sentence.
Look, this code show "error Only primitive types or enumeration types are supported in this context" error:
var query = db.TemplatesDocs.Where(x => x.Id_Template == idTmpl)
.Join(Utils.DocumentTypes, x => x.Id_Type, y => y.Id, (x, y) => new { tmpDoc = x, type = y } )
.ToList();
Look, this code is fixed the error:
var query = db.TemplatesDocs.Where(x => x.Id_Template == idTmpl).ToList()
.Join(Utils.DocumentTypes, x => x.Id_Type, y => y.Id, (x, y) => new { tmpDoc = x, type = y } )
.ToList();
My challenge is twofold:
To pick individual strings from an array of similar strings, but only if a boolean test has been passed first.
"Finally" I need to concatenate any/all of the strings generated into one complete text and the entire code must be in Swift.
Illustration: A back of the envelope code for illustration of logic:
generatedText.text =
case Int1 <= 50 && Int2 == 50
return generatedParagraph1 = pick one string at RANDOM from a an array1 of strings
case Int3 =< 100
return generatedParagraph2 = pick one string at RANDOM from a an array2 of strings
case Int4 == 100
return generatedParagraph3 = pick one string at RANDOM from a an array3 of strings
...etc
default
return "Nothing to report"
and concatenate the individual generatedParagraphs
Attempt: Code picks a random element within stringArray1, 2 and 3.
Example of what the code returns:
---> "Sentence1_c.Sentence2_a.Sentence3_b."
PROBLEM: I need the code to ONLY pick an element if it has first passed a boolean. It means that the final concatenated string (concastString) could be empty, just contain one element, or several depending on how many of the bools were True. Does anyone know how to do this?
import Foundation
var stringArray1 = ["","Sentence1_a.", "Sentence1_b.", "Sentence1_c."]
var stringArray2 = ["","Sentence2_a.", "Sentence2_b.", "Sentence2_c."]
var stringArray3 = ["","Sentence3_a.", "Sentence3_b.", "Sentence3_c."]
let count1 = UInt32(stringArray1.count)-1
let count2 = UInt32(stringArray2.count)-1
let count3 = UInt32(stringArray3.count)-1
var randomNumberOne = Int(arc4random_uniform(count1))+1
var randomNumberTwo = Int(arc4random_uniform(count2))+1
var randomNumberThree = Int(arc4random_uniform(count3))+1
let concatString = stringArray1[randomNumberOne] + stringArray2[randomNumberTwo] + stringArray3[randomNumberThree]
Okay, I didn't pass a Bool, but I show concatenating three random strings from a [String]. I ran this in a playground.
import Foundation
var stringArray = [String]()
for var i = 0; i < 100; i++ {
stringArray.append("text" + "\(i)")
}
func concat (array: [String]) -> String {
let count = UInt32(stringArray.count)
let randomNumberOne = Int(arc4random_uniform(count))
let randomNumberTwo = Int(arc4random_uniform(count))
let randomNumberThree = Int(arc4random_uniform(count))
let concatString = array[randomNumberOne] + array[randomNumberTwo] + array[randomNumberThree]
return concatString
}
let finalString = concat(stringArray)