I would like to know the most optimal/elegant solution of doing this. Basically i have 3 groups of data.
Each of the groups are inside another in a hierarchy. I'm using a dictionary within a dictionary to achieve this. Sample Code:
Dictionary<string,Dictionary<string,List<string>>> pro = new Dictionary<string,Dictionary<string,List<string>>>();
Dictionary<string,List<string>> part = new Dictionary<string,List<string>>();
List<string> foo = new List<string>();
List<string> bar = new List<string>();
foo.Add("foo1");
foo.Add("foo2");
foo.Add("foo3");
bar.Add("bar1");
bar.Add("bar2");
bar.Add("bar3");
part.Add("Part1", foo);
part.Add("Part2", bar);
pro.Add("First", part);
foreach (var pros in pro)
{
foreach (var parts in pros.Value)
{
foreach (var foos in parts.Value)
{
Console.WriteLine(foos);
}
}
}
I'm still new to c#, even though my code is working i still feel that there is a more elegant solution. Thanks in advance!
I think you should refactor this kind of code into simple wrapper-classes of Dictionaries. It's almost unreadable this way but if you make a wrapper-class for Parts/Pros, whatever your names are you get niecer looking and reading code and can put some utility functions inside the classes while hiding the implementation-detail of using Dictionaries.
you could use collection initializers for the construction part and LINQ for the print part.
Dictionary<string, Dictionary<string,List<string>>> pro = new Dictionary<string, Dictionary<string,List<string>>>()
{
{ "First", new Dictionary<string,List<string>>()
{
{ "Part1", new List<string>() { "foo1", "foo2", "foo3" } },
{ "Part2", new List<string>() { "bar1", "bar2", "bar3" } }
}
}
};
EDIT: provided new link for LINQ referring to another useful related post on stackoverflow.
Related
I have a page with multiple listViews. I have created some difference lists from DbModels and need to bind these lists to the ListViews. Here what I want is if (Particulars == Constants.TAG_OPENING_STOCK) a list adds to a ItemsTradingDebitList list.
and if(Particulars == Constants.TAG_PURCHASE) another list should add to ItemsTradingCreditList list.
I have tried creating a new list with values and added it to another list using AddRange. But this brings an error Object Reference not set to an instance of an object. list was empty
if (Particulars == Constants.TAG_OPENING_STOCK)
{
List<string> NewList = new List<string> { Particulars, Amount };
ItemsTradingDebitList.AddRange(NewList);
}
if(Particulars == Constants.TAG_PURCHASE)
{
List<string> NewList = new List<string> { Particulars, Amount };
ItemsTradingDebitList.AddRange(NewList);
}
if(Particulars == Constants.TAG_SALES)
{
List<string> NewList = new List<string> { Particulars, Amount };
ItemsTradingCreditList.AddRange(NewList);
}
My expected result is a list of all added lists. What I am getting is an error
I believe "Particulars", "Amount" are strings, if both are strings, then you can directly add it to the list, here no need to create new list
something like,
ItemsTradingDebitList.Add("Amount");
ItemsTradingDebitList.Add("Particulars");
As per error mentioned in question, I guess you have not initialized list i.e. ItemsTradingDebitList. If that is the case then before first if condition create an instance of ItemsTradingDebitList
like
List<string> ItemsTradingDebitList = new List<string>();
This will resolve your problem,
List<string> ItemsTradingDebitList = new List<string>(); //This might be missing in your code
if (Particulars == Constants.TAG_OPENING_STOCK)
{
ItemsTradingDebitList.Add("Amount");
ItemsTradingDebitList.Add("Particulars");
}
if(Particulars == Constants.TAG_PURCHASE)
{
ItemsTradingDebitList.Add("Amount");
ItemsTradingDebitList.Add("Particulars");
}
if(Particulars == Constants.TAG_SALES)
{
ItemsTradingDebitList.Add("Amount");
ItemsTradingDebitList.Add("Particulars");
}
#prasad-telkikar answer is correct. I just wanted to add a few remarks on your code.
it looks like Constants.TAG_OPENING_STOCK and Constants.TAG_PURCHASE are well... constants. Probably declared as
public static class Constants
{
public const string TAG_OPENING_STOCK = "TAG_something";
//...
}
So you can improve your code by using a switch instead:
var ItemsTradingDebitList = new List<string>() ;
switch (Particulars)
{
case Constants.TAG_OPENING_STOCK:
// ...
break;
case Constants.TAG_PURCHASE:
// ...
break;
// etc.
}
If they are not constant but static readonly string instead then you can't use them in the switch and can use a series of if { } else { } instead.
I managed to make a selection from the complex structure of the object, but only with the help of foreach, how can I avoid this foreach and solve my problem, just using LINQ?
var product = new List<ProductCrp>
{
new ProductCrp {
Strucutre = new StructureItem() {
CheckList = new CheckList() {
Checks = new List<Check>
{
new Check { NumberAsInt = "149" },
new Check { NumberAsInt = "260" },
new Check { NumberAsInt = null }
}
}
}
},
new ProductCrp {
Strucutre = new StructureItem() {
CheckList = new CheckList() {
Checks = new List<Check>
{
new Check { NumberAsInt = "261" },
new Check { NumberAsInt = "150" },
new Check { NumberAsInt = "260" }
}
}
}
}
};
string[] numbers = { "149" };
LINQ:
foreach (var item in product)
{
item.Strucutre.CheckList.Checks = item.Strucutre.CheckList.Checks.Where(w => numbers.Contains(w.NumberAsInt)).Select(w => w);
}
I managed to make a selection from the complex structure of the object, but only with the help of foreach, how can I avoid this foreach and solve my problem, just using LINQ?
You do not use LINQ for this purpose. You are using foreach correctly.
LINQ is for querying data. A foreach loop is about producing a side effect repeatedly. The body of your foreach is mutating a property of an object; that's an update and not a query, so you are doing it right. Using LINQ for that is wrong; don't do it.
Answers that say to, for instance, use ToList to force iteration of a query with a side effect are extremely bad style and result in code which is inefficient, hard to understand, hard to maintain, and works against the purpose of the query operators. NEVER abuse LINQ like that. We have a construct built into the language that means "perform this operation once per collection element", and it is called foreach. Use it.
Are you looking for something like this?
product.ForEach(item => item.Strucutre.CheckList.Checks = item.Strucutre.CheckList.Checks.Where(w => numbers.Contains(w.NumberAsInt)).Select(w => w).ToList());
I have two List<string> (listOfFullPaths containg full database paths for e.g. "C:\MyDir\SomeDir\SomeDatabase.mdf") and some other List<string> containing some of the names of the databases (only) ('listOfDatabases'). So each might include
List<string> listOfFullPaths = new List<string>()
{
"C:\MyDir\SomeDir\SomeDatabase1.mdf",
"C:\MyDir\SomeDir\SomeDatabase2.mdf",
"C:\MyDir\SomeDir\SomeDatabase3.mdf"
};
the other just
List<string> listOfFullPaths = new List<string>()
{
"SomeDatabase1",
"SomeDatabase2"
};
My question is what is the most efficent way of returning the full paths contined in listOfFullPaths that have their corresponding database in listOfDatabases?
Note: The answer is not something like
List<string> tmpList = new List<string>();
foreach (string database in listOfDatabases)
if (listOfFullPaths.Contains(database))
tmpList.Add(database);
listOfFullPaths.Clear();
listOfFullPaths = tmpList;
although this does what I want.
It sounds like you're rejecting the nested loop due to performance concern? If so then another way to approach this problem is the following.
var set = new Set<string>(listOfDatabases);
var list = new List<string>();
foreach (string fullPath in listOfFullPaths) {
var name = Path.GetFileNameWithoutExtension(fullPath);
if (set.Contains(name)) {
list.Add(fullPath);
}
}
listOfFullPaths = list;
I think if you want maximal efficiency you create a dictionary or hashed set with the database name as a key off the bat.
var listOfFullPaths = new List<string>()
{
"C:\MyDir\SomeDir\SomeDatabase1.mdf",
"C:\MyDir\SomeDir\SomeDatabase2.mdf",
"C:\MyDir\SomeDir\SomeDatabase3.mdf"
}.ToDictionary(k => Path.GetFilenameWithoutExtension(k));
Then you can loop through listOfDBs and look for the hash
listOfDbs.Where(w => listOfFullPaths.Contains(w));
In this case, you do two loops and take advantage of the built in indexing of the dictionary.
var fileNames = listOfFullpaths.Select(x => Path.GetFileNameWithoutExtension(x));
listOfFullPaths = listofDatabases.Where(x => fileNames.Contains(x)).ToList();
Note that if you know in advance that the listOfFullPaths is sorted by the database name, you can make it much more efficient.
Assuming both lists are populated, and you don't have access to the file on disk, then this linq will help you.
using System.Linq;
List<string> listOfFullPaths = new List<string>()
{
#"C:\MyDir\SomeDir\SomeDatabase1.mdf",
#"C:\MyDir\SomeDir\SomeDatabase2.mdf",
#"C:\MyDir\SomeDir\SomeDatabase3.mdf"
};
List<string> listOfDatabases = new List<string>()
{
"SomeDatabase1",
"SomeDatabase2"
};
var dbs = from path in listOfFullPaths
from db in listOfDatabases
where System.IO.Path.GetFileNameWithoutExtension(path) == db
select path;
If you want to do it efficiently in terms of speed (i.e. in O(n)) then JaredPar's answer is correct.
Here is a more idiomatic version:
var set = new HashSet<string>(listOfDbs);
List<string> result = listOfFullPaths
.Where(p => set.Contains(Path.GetFileNameWithoutExtension(p)))
.ToList();
I am comparing two dictionary(dic1 and dic2) with rule that get values from dic2 where key match but values does not match or key is missing in dic2.
Don’t need to iterate through dic2 for missing/different values in dic1.
Below code is working ok I would like to know is there any better way using .NET 2.0 (NO LINQ) .
if optimization is require which option is better?
Dictionary<string,List<foo>> dic1 = new Dictionary<string,List<foo>>();
Dictionary<string,List<foo>> dic2 = new Dictionary<string,List<foo>>();
dic1.add("1", new foo("a"));
dic1.add("2", new foo("b"));
dic1.add("3", new foo("c"));
dic1.add("3", new foo("c1"));
dic1.add("4", new foo("d"));
dic2.add("1", new foo("a"));
dic2.add("2", new foo("b1"));
dic2.add("3", new foo("c"));
dic2.add("3", new foo("c2"));
//I write code which allow duplicate key in dictionary
Option 1
foreach (KeyValuePair<string, List<foo>> var in dic1)
{
if (dic2.ContainsKey(var.Key))
{
List<foo> tempList = var.Value.FindAll(delegate(foo s)
{
return !dic2[var.Key].Contains(s);
});
result.AddRange(tempList);
}
else
{
result.Add(var.Value);
}
}
Option 2
List<string> list1key = new List<string>(dic1.Keys);
list1key.ForEach(delegate(string key)
{
if (dic2.ContainsKey(key))
{
List<foo> tempList = dic1[key].FindAll(delegate(foos)
{
return !dic2[key].Contains(s);
});
result.AddRange(tempList);
}
else
{
result.AddRange(dic1[key]);
}
});
You can speed things up with either option if you use TryGetValue when accessing dic2, so you only have to do a key lookup once.
Your first option looks simpler and possibly faster, i'd go with that.
Cheers
I would use Option 1. Here's a variation on it using TryGetValue instead of looking up dic2[var.Key] so many times:
foreach (KeyValuePair<string, List<foo>> var in dic1)
{
List<foo> dic2val;
if (dic2.TryGetValue(var.Key, out dic2val))
{
List<foo> tempList = var.Value.FindAll(delegate(foo s)
{
return !dic2val.Contains(s);
});
result.AddRange(tempList);
}
else
{
result.Add(var.Value);
}
}
It's pretty common - especially as you try to make your code become more data-driven - to need to iterate over associated collections. For instance, I just finished writing a piece of code that looks like this:
string[] entTypes = {"DOC", "CON", "BAL"};
string[] dateFields = {"DocDate", "ConUserDate", "BalDate"};
Debug.Assert(entTypes.Length == dateFields.Length);
for (int i=0; i<entTypes.Length; i++)
{
string entType = entTypes[i];
string dateField = dateFields[i];
// do stuff with the associated entType and dateField
}
In Python, I'd write something like:
items = [("DOC", "DocDate"), ("CON", "ConUserDate"), ("BAL", "BalDate")]
for (entType, dateField) in items:
# do stuff with the associated entType and dateField
I don't need to declare parallel arrays, I don't need to assert that my arrays are the same length, I don't need to use an index to get the items out.
I feel like there's a way of doing this in C# using LINQ, but I can't figure out what it might be. Is there some easy method of iterating across multiple associated collections?
Edit:
This is a little better, I think - at least, in the case where I have the luxury of zipping the collections manually at declaration, and where all the collections contain objects of the same type:
List<string[]> items = new List<string[]>
{
new [] {"DOC", "DocDate"},
new [] {"CON", "ConUserDate"},
new [] {"SCH", "SchDate"}
};
foreach (string[] item in items)
{
Debug.Assert(item.Length == 2);
string entType = item[0];
string dateField = item[1];
// do stuff with the associated entType and dateField
}
In .NET 4.0 they're adding a "Zip" extension method to IEnumerable, so your code could look something like:
foreach (var item in entTypes.Zip(dateFields,
(entType, dateField) => new { entType, dateField }))
{
// do stuff with item.entType and item.dateField
}
For now I think the easiest thing to do is leave it as a for loop. There are tricks whereby you can reference the "other" array (by using the overload of Select() that provides an index, for example) but none of them are as clean as a simple for iterator.
Here's a blog post about Zip as well as a way to implement it yourself. Should get you going in the meantime.
Create a struct?
struct Item
{
string entityType;
string dateField;
}
Pretty much the same as your Pythonic solution, except type-safe.
This is realy a variation on the other themes, but this would do the trick also...
var items = new[]
{
new { entType = "DOC", dataField = "DocDate" },
new { entType = "CON", dataField = "ConUserData" },
new { entType = "BAL", dataField = "BalDate" }
};
foreach (var item in items)
{
// do stuff with your items
Console.WriteLine("entType: {0}, dataField {1}", item.entType, item.dataField);
}
You can use the pair and a generic List.
List<Pair> list = new List<Pair>();
list.Add(new Pair("DOC", "DocDate"));
list.Add(new Pair("CON", "ConUserDate"));
list.Add(new Pair("BAL", "BalDate"));
foreach (var item in list)
{
string entType = item.First as string;
string dateField = item.Second as string;
// DO STUFF
}
Pair is part of the Web.UI, but you can easily create your own custom class or struct.
If you just want to declare the lists inline, you can do that in one step:
var entities = new Dictionary<string, string>() {
{ "DOC", "DocDate" },
{ "CON", "ConUserDate" },
{ "BAL", "BalDate" },
};
foreach (var kvp in entities) {
// do stuff with kvp.Key and kvp.Value
}
If they're coming from other things, we have a bunch of extension methods to build dictionaries from various data structures.