I'm working with c#, and I have working code where I repeated same code every line, and that's because I'm creating list, conversions , extracting data etc.
So I have multiple foreach clauses,multiple lists, multiple conversions of list to datatables. My question is, what can I do to re-factorize this in order to have clean code?
Code:
private void BtnLoadReport_Click(object sender, EventArgs e)
{
var db = new SQLDataMgr();
List<string> DesignStatusList = new List<string>();
List<string> ShopStatusList = new List<string>();
List<string> CustomerTypeList = new List<string>();
List<string> CustomerList = new List<string>();
List<string> ResellerList = new List<string>();
List<string> StateList = new List<string>();
List<string> ProjectManagerList = new List<string>();
List<string> SalesRepresentativeList = new List<string>();
var checkedDesignStatus = cboDesignStatus.CheckBoxItems.Where(x => x.Checked);
var checkedShopStatus = cboShopStatus.CheckBoxItems.Where(x => x.Checked);
var checkedCustomerType = cboShopStatus.CheckBoxItems.Where(x => x.Check
var checkedCustomer = cboShopStatus.CheckBoxItems.Where(x => x.Checked);
var checkedReseller = cboShopStatus.CheckBoxItems.Where(x => x.Checked);
var checkedState = cboShopStatus.CheckBoxItems.Where(x => x.Checked);
var checkedProjectManager = cboShopStatus.CheckBoxItems.Where(x => x.Checked);
var checkedSalesRepresentative = cboShopStatus.CheckBoxItems.Where(x => x.Checked);
foreach (var i in checkedDesignStatus)
{
DesignStatusList.Add(i.Text);
}
foreach (var i in checkedShopStatus)
{
ShopStatusList.Add(i.Text);
}
foreach (var i in checkedCustomerType)
{
CustomerTypeList.Add(i.Text);
}
foreach (var i in checkedCustomer)
{
CustomerList.Add(i.Text);
}
foreach (var i in checkedReseller)
{
ResellerList.Add(i.Text);
}
foreach (var i in checkedState)
{
StateList.Add(i.Text);
}
foreach (var i in checkedProjectManager)
{
ProjectManagerList.Add(i.Text);
}
foreach (var i in checkedSalesRepresentative)
{
SalesRepresentativeList.Add(i.Text);
}
DataTable designStatusParameters = ToStringDataTable(DesignStatusList);
DataTable shopStatusParameters = ToStringDataTable(ShopStatusList);
DataTable customerTypeParameters = ToStringDataTable(CustomerTypeList);
DataTable customerParameters = ToStringDataTable(CustomerList);
DataTable resellerParameters = ToStringDataTable(ResellerList);
DataTable stateParameters = ToStringDataTable(StateList);
DataTable projectManagerParameters = ToStringDataTable(ProjectManagerList);
DataTable salesRepresentativerParameters = ToStringDataTable(SalesRepresentativeList);
}
Change ToStringDataTable to an Extension Method. Then:
var designStatusParameters = cboDesignStatus.CheckBoxItems.Where(x => x.Checked)
.Select(i => i.Text).ToList().ToStringDataTable();
You can even write the extension method for your control in a way that performs filtering, projection and converting to data table for you then you can have:
var designStatusParameters = cboDesignStatus.ToStringDataTable();
You can cut out all the foreach loops by using Linq's Select():
For example:
var DesignStatusList =
cboDesignStatus.CheckBoxItems
.Where(x => x.Checked)
.Select(i => i.Text)
.ToList();
That will leave you with a List<string> containing all the Text properties from the checked CheckBoxes.
You could even skip declaring the list and combine that with your DataTable creation lines:
var designStatusParameters = ToStringDataTable(
cboDesignStatus.CheckBoxItems
.Where(x => x.Checked)
.Select(i => i.Text)
.ToList());
I would suggest putting this in a method of its own rather than repeating this over and over for each group of checkboxes.
Just keep in mind that less lines of code doesn't mean faster performance. It still has to iterate through the lists to find the right values. But it is much more readable than being hit in the face with a wall of repetitive code.
You don't need those many foreach rather re-factor to a single method like
public void AddToList(List<string> listToAdd, List<CheckBox> listToIterate)
{
foreach (var i in listToIterate)
{
listToAdd.Add(i.Text);
}
}
You are taking unnecessary expicit steps making your code way more verbose than it should be.
You are doing in separate steps:
Make an empty list.
Filter data
Iterate filtered data and add it to the empty list.
When you can do:
var newList =
someCollection.Where(someFilter)
.Select(someProjection)
.ToList();
That will make the code much more readable.
And, if you are in the mood, you can even make a helper method where you pass in someCollection, someFilter and someProjection and returns a list of something.
The code below isn't quite right, but I'd recommend that you build one generic method that returns a DataTable and you call that method over and over. Something like this:
public DataTable getDataTable( CheckBox yourCheckBox)
{
DataTable returnTable = new DataTable();
List<string> tempList = new List<string>();
var checkedDesignStatus = yourCheckBox.CheckBoxItems.Where(x => x.Checked);
foreach (var i in checkedDesignStatus)
{
tempList.Add(i.Text);
}
returnTable = ToStringDataTable(tempList);
return returnTable;
}
Create method (maybe local method?)
DataTable CheckedToDataTable(YourContainerType container)
=> ToStringDataTable(container.CheckBoxItems.Where(x => x.Checked).Select(x => x.Text).ToList());
Related
I created a Yaml that looks like this:
Directories:
- ./Libraries:
- DLLList.yml
- ./Output:
- None
Now I deserialized that yaml into a list of Objects:
List<object> allDirectoriesList = new List<object>();
List<string> allFileNames = new List<string>();
using (var reader = new StringReader(File.ReadAllText("./FileConfig.yml")))
{
allDirectoriesList = deserializer.Deserialize<dynamic>(reader)["Directories"] as List<Object>;
}
foreach (var directory in allDirectoriesList)
{
var directoryAsDictionary = (Dictionary<object, object>)directory;
List<object> list = directoryAsDictionary.Select(kvp => kvp.Value).ToList();
IEnumerable<string> _fileList = list.Select(i => i.ToString());
List<string> fileList = _fileList.ToList<string>();
for (int i = 0; i < fileList.Count(); i++)
{
var x = (string)list[i];
}
}
directory is an object of type Dictionary where I converted it into a List in this part:
var directoryAsDictionary = (Dictionary<object, object>)directory;
List<object> list = directoryAsDictionary.Select(kvp => kvp.Value).ToList();
This list contains 1 object of type string, where the filename is stored. But I can't get these strings out of the objects. If I cast them, or convert them ToString(), I always get "System.Collections.Generic.List`1[System.Object]", but it has to be "DLLList.yml" in this case
Assuming you are using YamlDotNet:
List<object> allDirectoriesList = new List<object>();
using (var reader = new StringReader(File.ReadAllText("./FileConfig.yml")))
{
allDirectoriesList = new DeserializerBuilder().Build().Deserialize<dynamic>(reader)["Directories"] as List<object>;
}
foreach (var directory in allDirectoriesList)
{
var directoryAsDictionary = (Dictionary<object, object>)directory;
List<object> list = directoryAsDictionary.SelectMany(kvp => (List<object>)kvp.Value).ToList();
List<string> _fileList = list.Select(Convert.ToString).ToList();
foreach(var file in _fileList)
Console.WriteLine($"Item: {file} found in {Convert.ToString(directoryAsDictionary.Keys.First())}");
}
Basically you were trying to turn the dictionary value to a string, but it was a List. By using SelectMany, it can flatten all the lists into one and use that. There were a few redundant casts, which I've also removed. For future reference, try to make your structures as simple as possible and deserialise them into structs/classes - you'll find this a lot easier that way.
I have method to save all values from table to txt file:
UserDataDBsDataContext dataContext = new UserDataDBsDataContext();
List<UserData> usersL = (from u in dataContext.UserDatas
select u).ToList();
var properties = typeof(UserData).GetProperties();
var userValues = new List<string>();
foreach (var user in usersL)
{
var values = new List<object>();
foreach (var property in properties)
{
object value = property.GetValue(user, null);
values.Add(value);
}
userValues.Add(string.Join(",", values));
}
File.WriteAllLines("my_data.txt", userValues);
Now I have two query and I want to do exactly the same, so I tried to create separate method responsible for looping table values.
Loop Method:
public void loopProp(PropertyInfo[] properites, List<string> addedValues)
{
foreach (var qrl in ...........)
{
var values = new List<object>();
foreach (var property in properites)
{
object value = property.GetValue(qrl, null);
values.Add(value);
}
addedValues.Add(string.Join(",", values));
}
File.WriteAllLines("my_passed_data.txt", addedValues);
}
But I don't know, how to pass query result(ar or ud):
My code:
List<AutoRef> ar = (from a in rjdc.AutoRefs
select a).ToList();
List<UserDataRef> ud = (from u in rjdc.UserDataRefs
select u).ToList();
var propertiesAutoRef = typeof(AutoRef).GetProperties();
var autoValues = new List<string>();
var propertiesUserRef = typeof(UserDataRef).GetProperties();
var userValues = new List<string>();
//loopProp(propertiesAutoRef, autoValues);
//loopProp(propertiesUserRef, userValues);
Answering your concrete question. You should make the method generic and pass the source as IEnumerable<T>:
public void loopProp<T>(IEnumerable<T> source, PropertyInfo[] properites, List<string> addedValues)
{
foreach (var qrl in source)
{
// ...
}
File.WriteAllLines("my_passed_data.txt", addedValues);
}
Usage:
loopProp(ar, propertiesAutoRef, autoValues);
loopProp(ud, propertiesUserRef, userValues);
Probably you should pass the file path argument as well instead of hardcoding it inside the method.
I'm adding new items to a list from a IEnumerable (query.Roles).
var query = GetRoles();
var vm = new CreateUserViewModel();
vm.Role = new List<CreateUserViewModel.Item>();
foreach (var Role in query.Roles)
{
vm.Role.Add(new CreateUserViewModel.Item
{
Label = Role.Label,
RoleNumber = Role.RoleNumer
});
}
How i can do the 'Add' to the list with Linq?
AddRange should do it for you:
vm.Role.AddRange(query.Roles.Select(r => new CreateUserViewModel.Item
{
Label = r.Label,
RoleNumber = r.RoleNumer
}));
AddRange takes an IEnumerable parameter and adds each item to the collection.
vm.Role = query
.Roles
.Select(r=>new CreatUserViewModel
.Item{Label = r.Label,
RoleNumber = r.RoleNumber})
.ToList();
I need to know how to implement the below logic to get the list of items with the help of LINQ without using foreach. Also, I need to exclude those matching Items from item List after adding the item into new list.
Code
List<StockResult> Stockres = new List<StockResult>();
foreach (var stkitms in item)
{
if (Db.Stk.Any(a => a.INo == stkitms.ItemNum))
{
StockResult ss = new StockResult();
ss.ItemNumber = stkitms.ItemNum;
ss.FileName = stkitms.FileName;
Stockres.Add(ss);
}
}
Any solution to this will be appreciated.
You could try this one:
List<StockResult> Stockres = item.Where(x=>Db.Stk.Any(a => a.INo == x.ItemNum))
Select(x=> new StockResult()
{
ItemNumber = x.ItemNum,
FileName = x.FileName
}).ToList();
I am curious if there is a way where I can capture the lower lines (the creation of the dictionary and loop to add values) in the linq statement itself. Right now the select new returns a new anonymous type but I am wondering if there is a way to make it return a Dictionary with all the values pre-populated.
XDocument reader = XDocument.Load("sl.config");
var configValues = from s in reader.Descendants("add") select new { Key = s.Attribute("key").Value, s.Attribute("value").Value };
Dictionary<string, string> Settings = new Dictionary<string, string>();
foreach (var s in configValues)
{
Settings.Add(s.Key, s.Value);
}
Try Enumerable.ToDictionary extension method.
XDocument reader = XDocument.Load("sl.config");
var Settings = reader.Descendants("add")
.ToDictionary(s => s.Attribute("key").Value, s => s.Attribute("value").Value);
var = XDocument.Load("sl.config").Descendants("add").ToDictionary
(x => x.Attribute("key"). Value, x => x.Attribute("value"). Value);