C# Loop through a collection and assign each object to a variable - list

I am sure this is quite simple but I cannot seem to get this right.
I have a built up a ICollection of Users. This collection could have 1 or many.
I now want to loop through this collection and assign a variable to each user that I can then use to fill in a spread sheet.
At present I have this code :
string username = null;
foreach (var user in dailyReport)
{
username = user.UserName;
}
Cell cell= worksheet.Cells["B5"];
cell.PutValue(username);
Now obviously this just puts the last user of the collection into cell B5!
How do I collect all user.UserNames so I can place them in B5, B6, B7 and so on????

To get a list of the user names:
List<string> userNames = dailyReport.Select( x=> x.UserName).ToList();
Alternatively you can just do the assignment in the loop directly:
int index = 5;
foreach (var user in dailyReport)
{
Cell cell= worksheet.Cells["B"+ index.ToString()];
cell.PutValue(user.UserName);
index++;
}

You need to put the value inside the foreach loop. The specific thing you are asking for--getting a different variable for every value in the collection--is not possible, and isn't what you want anyway.
string column = "B"; // or whatever your column is
int row = 1; // or whatever your starting value is
foreach (var user in dailyReport)
{
string username = user.UserName;
string cellAddress = String.Format("{0}{1}", column, row);
Cell cell = worksheet.Cells[cellAddress];
cell.PutValue(username);
row++; // make sure to increment the row, or every username goes in the same cell
}

int i = 5;
foreach (var user in dailyReport)
{
worksheet.Cells["B" + (i++).ToString()].PutValue(user.UserName);
}

Related

Lookup and clear cell content google script

Sorry, I am new to scripting and this is probably a simple one to write; apologies!
I am trying to create a script that gets a range of order numbers in a column, then looks it up on another tab, and then clears a certain cell in that row.
I can get the script to look up the 1st ordernumber at the top of the list andd clear the cell I need clearing, but I cannot work out how to lookup and clear more than one order number at a time.
The logger returns all the values, so I think I need a loop, but I do not knwo where to start with that.
This is for a work project, but I have created a basic sample sheet to play around with:
https://docs.google.com/spreadsheets/d/19koKxFcOfWRz0mEaYs_lHQFgBrU19kDoeYaBY2WBe98/edit?usp=sharing
Can anyone help??
Thanks,
John
Here is the script so far:
function clearpostagecells(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Order Control');
var sheet2 = ss.getSheetByName('Order Database');
var find = sheet1.getRange(2,2,sheet1.getLastRow()-1).getDisplayValues();
Logger.log(find)
var values = sheet2.getRange(2, 1, sheet2.getLastRow()-1).getDisplayValues();
for (var i = 0; i < values.length; i++) {
var row = "";
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == find) {
row = i+2;
sheet2.getRange(row,7).clearContent();
}
}
}}

Instantiating at random based on word

I have a word = "CAT"
I have 26 alphabet prefabs A - Z - prefab array
I want to instantiate 6 prefabs from the prefab at random
so B,L,T,C,A,T - in any random order
but I need it to include the letters that makeup the word cat
My first thought was to use a dictionary/hashmap to map the word cat to the position in the array i.e C = [2] A = [0] T[]
but the hashmap/dictionary only takes a key value pair but I am providing it with more than one value the 3 letters needed
This is being programmed in unity so my prefabs are already in the letters array its just getting them to include my spelling.
public GameObject[] letters;
void Start() {
int x;
/* for loop execution */
for (x = 0; x < 5; x = x + 1)
{
CreateCubes();
}
}
public void CreateCubes()
{
GameObject obj = Instantiate(letters[Random.Range(0, 26)]);
obj.transform.position = new Vector3(
);
Assuming you have all prefabs in a GameObject[] array you could use this to auto-populate a dictionary
public GameObject[] prefabs = new GameObject[26];
private Dictionary<char, GameObject> CharToPrefab = new Dictionary<char, GameObject>(26);
private void Start()
{
for (var i = 0; i < 27; i++)
{
// add 0 to 26 to the start character A
// results in A-Z
CharToPrefab[(char)('A' + i)] = prefabs[i];
}
}
Than you can access a certain prefab by calling
CharToPrefab[character];
For generating the instances (simplest version without taking any doubles into account) you could do e.g.
public void RandomLetters(string word)
{
// 1. spawn the minimum required letters to build the word
foreach (var letter in word)
{
var obj = Instantiate(CharToPrefab[letter]);
// maybe use obj for something e.g. shuffel all instantiated objects positions
}
// 2. fill the rest with random letters
// assuming always same amount as word letters
var rand = new System.Random();
foreach (var letter in word)
{
// pics a number from 0 to 26
// and adds it to the char -> results in A-Z
var randomChar = (char)('A' + rand.Next(0, 27));
var obj = Instantiate(CharToPrefab[randomChar]);
// maybe use obj for something e.g. shuffel all instantiated objects positions
}
}

Best way to run through the entire sheet looking for a text in Google Sheets

I want to create a script that needs to find a certain string and replace it automatically. I've managed to do that, but it takes over 1 minute to run through all rows and columns to find it.
This is what I'm doing now:
for (i=1; i<=rows; i++) {
for (j=1; j<=cols; j++) {
cell = content.getCell(i, j).getValue();
if (content.getCell(i, j).getFormula()) {continue;}
try {
cell = cell.replace (find, replace);
content.getCell(i, j).setValue(cell);
}
catch (err) {continue;}
}
}
The built-in method replaces a text instantly, so I assume there is a better way to approach this. Any ideas?
Instead of retrieving each cell from the sheet one by one, use getDataRange() and getValues() to get all of the data in an array in one call, then perform your search on the array.
Then, depending on if you might have live users editing at the same time your script is run or not, you can either replace the values within the array and re-write the entire sheet with setValues(), or you can use setValue() to update the specific cells with matches one by one as you are currently doing.
See:
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getdatarange
https://developers.google.com/apps-script/reference/spreadsheet/range#getvalues
Try this...
for (i=1; i<=rows; i++) {
for (j=1; j<=cols; j++) {
cellLoc = content.getCell(i, j);
cellValue = cellLoc.getValue();
if (cellLoc.getFormula()) {continue;}
try {
cellValue = cellValue.replace (find, replace);
cellLoc.setValue(cell);
}
catch (err) {continue;}
}
}
I took off every instance of content.getCell(i,j) and instead stored it into cellLoc. That way every time you need content.getCell(i,j), the program doesn't have to find the cell in the content, it can just look what the value of cellLoc is. Let me know if it works and if time has improved...
Well, I don't see much difference between this approach and my first one, but it replaces all matches instantly now:
function findReplace() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var i, j, find, replace;
find = "hello";
replace = "bravo!!";
for (i = 0; i < values.length; i++) { //Run through all rows
for (j = 0; j < values[i].length; j++) { //Run through all columns
if (values[i][j] == find){
values[i][j] = replace;
}
}
}
range.setValues(values);
}

Accessing a Combobox inside a dataGridView Column?

I'm working on a scheduling program, and inside the dataGridView, we have a few ComboBox Columns that are populated by 3 entries upon creation, but I wanted to be able to add more as the user creates them, but I have no idea how you would access the combobox data. Any help is appreciated!
// this is initialized in a separate part.
/* System::Windows::Forms::DataGridView^ dataGridView;*/
System::Windows::Forms::DataGridViewComboBoxColumn^ newCol =
(gcnew System::Windows::Forms::DataGridViewComboBoxColumn());
dataGridView->Columns->AddRange(gcnew cli::array< System::Windows::Forms::DataGridViewComboBoxColumn^ >(1) {newCol});
// add the choices to the boxes.
newCol->Items->AddRange("User inputted stuff", "More stuff", "Add New...");
Solution
If you have access to the data from the user entry and you know the column index for the DataGridViewComboBoxColumn, you should be able to just do the following wherever needed:
DataGridViewComboBoxColumn^ comboboxColumn = dataGridView->Columns[the_combobox_column_index];
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
}
Comments Response
how could you change the selected index of that combobox (the one that
the edit was triggered on)? [...] we want it so that when the new item
is added the selected index is set to that new item).
Couple of ways come to mind.
Add a single line within the if-statement of the above code. This will set the default displayed value for each DataGridViewComboBoxCell in the DataGridViewComboBoxColumn.
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
comboboxColumn->DefaultCellStyle->NullValue = "the new user entry";
}
Pros: Clean, efficient. Previous user-selected values are left intact. The cell's FormattedValue will display the new user value by default if no other selection has been made.
Cons: Doesn't actually set a cell's selected value, so Value will return null on cells not explicitly user-selected.
Actually set the value of certain cells (based on your criteria) to the user-added value.
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
for (int i = 0; i < dataGridView->Rows->Count; i++)
{
DataGridViewComboBoxCell^ cell = dataGridView->Rows[i]->Cells[the_combobox_column_index];
if ( cell != nullptr /* and your conditions are met */ )
{
cell->Value = "the new user entry";
}
}
}
Pros: The Value of targeted cells is actually set to the new user value.
Cons: Logic deciding which cells should be affected is more complicated.

ORM Entity - Remove a record, and add a property

I am putting together a store finder which works on a radius from a postal code. I have done this many times using standard Queries and QoQs, but now trying to put one together using cf9 ORM... but seemed to have reached the limit of my capabilities with the last bit.
I am pulling out an entity and processing it. At the end, I need to:
a. Remove a record if it doesn't meet a certain criteria (distance is greater than specified by user)
OR b. Add a new property to the record to store the distance.
So at the end, all I want in my entity are those stores that are within the range specified by the user, with each record containing the calculated distance.
Best way to see what I am trying to do is to view the full function
Any suggestions greatly appreciated!!
public function getByPostcodeRadius(required postcode="",
required radius=""){
//set some initial vals
rs = {};
geo = New _com.util.geo().init();
local.postcodeGeo = geo.getGeoCode("#arguments.postcode#, Australia");
local.nearbyStores = "";
local.returnStores = {};
//load stores
local.stores = entityload("stores");
//loop over all stores and return list of stores inside radius
for(i=1; i <= ArrayLen(local.stores); i++){
store = {};
store.id = local.stores[i].getID();
store.geoCode = local.stores[i].getGeoCode();
store.Lat = ListgetAt(store.geoCode,1);
store.Lng = ListgetAt(store.geoCode,2);
distance = geo.getDistanceByGeocode(local.postcodeGeo.Lat,local.postcodeGeo.Lng,store.Lat,store.Lng);
//************************
//HERE IS WHERE I AM STUCK.
if (distance LT arguments.radius){
//here I need to add a property 'distance' and set it's value
local.stores[i].distance = distance; // this adds it to the object, but not with the PROPERTIES
} else {
// here i need to remove the store from the object as it's distance was greater than the one passed in
arrayDeleteAt(local.stores,i); //this clearly isn't working, as positions are changing with each loop over
}
}
return local.stores;
}
If you delete an object from an array it will mess up your loop.
Try either looping backwards:
var i = arrayLen( local.stores );
for ( i; i == 0; i-- )
Or looping like this
for ( var local.store in local.stores )
(That's rough code and may need some tweaks)
I'd approach this from a different angle:
1) Instead of deleting from the array of all stores those that don't match, I'd build an array of those that do and return that.
2) If the distance is specific to each query and not a property of the store object, then I wouldn't try adding it to the store, but just "associate" it with the specific data I'm returning for this search.
Putting to the 2 together, I'd return an array of structs containing the store object and its distance from the requested postcode. (You could just return a single struct of the store object and distance, with the store ID as key, but I prefer working with arrays.)
Here's how I'd code it (not tested because I don't have your geo class or entity code):
public array function getByPostcodeRadius(required postcode="", required radius=""){
hint="I return an array of structs each containing a store object within the requested radius and its distance from the requested post code"
// Geo settings
local.geo = New _com.util.geo().init();
local.postcodeGeo = local.geo.getGeoCode("#arguments.postcode#, Australia");
// initialise the array of structs to return, which will contain stores within the requested radius and their distance from the postcode
local.nearbyStores = [];
//load all stores
local.stores = entityload("stores");
//loop over all stores and add those inside the radius to the return array with their distance
for( var storeObject in local.stores ){
// determine the lat-lng for this store
local.storeLat = ListgetAt(storeObject.getGeoCode(),1);
local.storeLng = ListgetAt(storeObject.getGeoCode(),2);
// get the distance from the requested postcode
local.distance = local.geo.getDistanceByGeocode(local.postcodeGeo.Lat,local.postcodeGeo.Lng,local.storeLat,local.storeLong);
if (local.distance LT arguments.radius){
// create a struct of the store object and its distance and add to the nearby stores array
local.thisStore = {
store = storeObject
,distance = local.distance
};
ArrayAppend( local.nearbyStores,local.thisStore );
}
}
return local.nearbyStores;
}