I am still a beginner with C# so I am sure I am missing some fundamental concept here but I am struggling with this.
I am trying to make a new collection from two existing collections.
The first is a Dictionary<string, someModel[]>
The second is a Dictionary<string, string>
I am trying to find matches between the two dicts keys and if they match make a new myOtherModelwith the values from the two dicts, but if they don't match I still want to make new myOtherModel but with an empty string for the missing value then add all those new myOtherModel will be added to a list.
The new myModel object will be one of two scenarios
For example: Dict1.Keys = 1,2,3,4....100. Dict2.Keys = 5,9,27,55
myList.Add(new myModel = {1, "", someModel[]}) //did not find a match
myList.Add(new myModel = {5, dict2.MatchingValue, someModel[]}) // did find a match
So basically, compare two dictionaries, for each item in the larger dictionary, make a new myModel with the item's values (one of them will be empty). But if that item's key matches a key in the other dictionary, grab the second dictionary's value and slap that in the new myModel
I tried messing around with a Tuples but I wasn't able to manipulate them how I wanted to.
This is what I have so far, but instead of giving me 490 items (the count from dict1) I get the 44k (the amount of the two being multiplied together)
foreach (var pair in dict1)
{
foreach (var item in dict2)
{
if (item.Key == pair.Key)
{
var x = new myModel()
{
prop1 = item.Value,
prop2 = pair.Key,
prop3 = pair.Value
};
myListOfModels.add(x);
}
else
{
var x = new myModel()
{
prop1 = "",
prop2 = pair.Key,
prop3 = pair.Value
};
myListOfModels.add(x);
}
}
}
You're looping through the second collection each time you iterate through the first collection which is why you're seeing too many results. You can easily simplify your code with a simple bit of Linq...
foreach (var pair in dict1)
{
// Get the matched value. If there isn't one it should return the default value for a string.
var matchedValue = dict2.Where(x => x.Key == pair.Key).Select(x => x.Value).SingleOrDefault();
var x = new myModel()
{
prop1 = matchedValue,
prop2 = pair.Key,
prop3 = pair.Value
};
myListOfModels.add(x);
}
Related
I have (for certain reasons not to get into now...) a List of the following structure:
List1<Dictionary1<string, List2<Dictionary2<string, string>>>>
(I added the 1 and 2 naming for clarity).
I want to iterate over List1 and sum up Dictionary1, so that all values of identical keys in Dictionary2 will add up.
For example if each Dictionary1 item contains a Dictionary2:
{ "Price", 23},
{ "Customers", 3}
then I want to iterate over all List2 elements, and over all List1 elements, and have a final dictionary of the total sum of all prices and customers as a single key for each category:
{ "Price", 15235},
{ "Customers", 236}
I hope that's clear.. In other words, I want to sum up this double-nested list in a way that I'm left with all unique keys across all nested dictionaries and have the values summed up.
I believe it can be done with LINQ, but I'm not sure how to do that..
This may be the ugliest thing I've ever written, and makes some assumptions on what you're doing, but I think this gets you what you want:
var query = from outerDictionary in x
from listOfDictionaries in outerDictionary.Values
from innerDictionary in listOfDictionaries
from keyValuePairs in innerDictionary
group keyValuePairs by keyValuePairs.Key into finalGroup
select new
{
Key = finalGroup.Key,
Sum = finalGroup.Sum(a => Convert.ToInt32(a.Value))
};
Where x is your main List.
Ok, so it looks like that you were attempting to create an Dictionary of Items with various properties (Cost, Customers, etc...), which begs the question: why not just create a class?
After all, it would be pretty simple to turn your dictionary of dictionary of items into a single dictionary of properties, such as below.
public class ItemProperties
{
public double Price {get; set;} = 0;
public int Customers {get; set;} = 0;
//Whichever other properties you were thinking of using.
}
static ItemProperties AddAll(Dictionary<string, ItemProperties> items)
ItemProperties finalitem = new ItemProperties();
{
foreach (var item in items)
{
finalitem.Price += item.Price;
finalitem.Customers += item.Customers;
//Repeat for all other existing properties.
}
return finalitem;
}
Of course, this only works if the number and kind of properties is immutable. Another way to approach this problem would be to use TryParse to find properties in Dictionary2 that you think can be added. This is problematic however, and requires some good error checking.
static Dictionary < string, string > AddNestedDictionary(Dictionary < string, Dictionary < string, string > items) {
Dictionary < string, string > finalitem = new Dictionary < string, string > ();
foreach(var item in items) {
foreach(var prop in item) {
if (!finalitem.ContainsKey(prop.Key)) {
finalitem.Add(prop);
}
double i = 0;
if (Double.TryParse(prop.Value, out i)) {
finalitem[prop.Key] += i;
}
}
}
return finalitem;
}
Again, not the best answer compared to the simplicity of a static class. But that would be the price you pay for nonclarity.
I'm using foreach to transfer data from list to another but when adding value updated automatically to last value added. For example:
list1 = [1,2,3]
list2 = new List<Model>()
foreach(var item in list1) {
list2.Add(item)
}
the result in list2 is [ 3, 3, 3]
Actually example is below :
var _sizes = new List<ProductsSize>();
var _size = new ProductsSize();
if (model.Dynamic_ProductsSize.Count > 0)
{
foreach (var item in model.Dynamic_ProductsSize)
{
_size.SizeId = item;
_sizes.Add(_size);
}
}
model.ProductsSize = _sizes.ToList();
I need to know why it only takes the last item and what is the solution for this case
You only have one ProductsSize object:
var _size = new ProductsSize();
And you keep modifying that same object. All references to that object, including any list elements it's been added to, get updated when you modify that one object.
Instead, create your new object in the loop:
foreach (var item in model.Dynamic_ProductsSize)
{
var _size = new ProductsSize();
_size.SizeId = item;
_sizes.Add(_size);
}
That way each element in the list is a new object instead of the same object added multiple times.
Side note, you have a few things in the code which aren't necessary. Checking the length before the loop, for example, as well as converting a list to a list at the end.
In fact, I imagine all of the code shown can be shortened to simply this:
model.ProductsSize = model.Dynamic_ProductsSize.Select(p => new ProductsSize { SizeId = p }).ToList();
In which case you're also just converting one model property to another model property. Why not put this logic in the model itself and skip the whole thing?
public IEnumerable<ProductsSize> ProductsSize
{
get { return this.Dynamic_ProductsSize.Select(p => new ProductsSize { SizeId = p });
}
Unless there's a particular reason you want the same data twice in two different properties that isn't clear from this code, having one set of data and just different views/calculations/etc. of that data is often preferred.
Create a new object before adding it to the list. You can use the object initializer syntax to keep it concise:
if (model.Dynamic_ProductsSize.Count > 0)
{
foreach (var item in model.Dynamic_ProductsSize)
{
_sizes.Add(new ProductsSize(){SizeId = item});
}
}
Let's have a simple class with 2 fields
public class Sample
{
public int IdOfSample;
public string SampleName;
}
And another using this one
public class ListOfSamples
{
public int IdOfList;
public List<Sample> SampleList;
}
And finally, since we will use a couple of different ListOfSamples, make a list of them:
public static List<ListOfSamples> FinalList = new List<ListOfSamples>();
Now the problem:
I create a new Sample (let's call it NewItem), with some name and Id. I want to check if there's a ListOfSamples in my FinalList that as the same Id as the NewItem I have. Otherwise create new ListOfSamples in the FinalList with the IdOfList = NewItem.IdOfSample.
I think I got the first part which checks if you should add a new list (ie. a ListOfSamples with specified IdOfList does not exist:
Sample NewItem = new Sample()
{
IdOfSample = 12345,
SampleName = "Some name"
};
int index = FinalList.FindIndex(f => f.IdOfList == NewItem.IdOfSample);
if (!FinalList.Any() || index == -1)
{
ListOfSamples NewList = new ListOfSamples()
{
IdOfList = NewItem.IdOfSample,
SampleList = new List<Sample>()
};
NewList.SampleList.Add(NewItem);
FinalList.Add(NewList);
}
Now, I'm trying to construct a statement, that, if the list with specified Id already exists in the FinalList, just add the new item to it, but so far I think my limited experience with LINQ is showing, nothing I try seems to work.
So:
If there exists a ListOfSamples with IdOfList == NewItem.IdOfSample in FinalList, then add NewItem to that ListOfSamples.
How about
if (!FinalList.Any() || index == -1)
...
else
{
FinalList[index].SampleList.Add(NewItem);
}
If you just wanted to check whether the list item existed, a suitable LINQ statement could be:
if (FinalList.Any(l => l.IdOfList == NewItem.IdOfSample))
{
// ...
}
Given you want to work on the item then you could attempt to retrieve it as follows:
var existingList = FinalList.SingleOrDefault(l => l.IdOfList == NewItem.IdOfSample);
if (existingList != null)
{
existingList.Add( ... );
}
Though perhaps it's worth thinking about using a HashSet of lists if you want to guarantee uniqueness...
if i understand it right ...
// search for the list with the given Id
var listOfSamples = finalList.Where(fl => fl.IdOfList == newItem.IdOfSample).FirstOrDefault();
if (listOfSamples == null)
{
// not found
// add new List with the new item in final list
finalList.Add(new ListOfSamples {IdOfList = newItem.IdOfSample, SampleList = new List<Sample>{newItem}} );
}
else
{
// found
// add the new item into the found list
listOfSamples.SampleList.Add(newItem);
}
If you replace ListOfSamples with a Dictionary<int, List<Sample>> then you will gain the ability to do a lookup in O(1) time and guarantee that the ids at the top level are unique. and then you can just add stuff like this.
Dictionary<int, List<Sample>> FinalList = new Dictionary<int, List<Sample>>();
Sample NewItem = new Sample()
{
IdOfSample = 12345,
SampleName = "Some name"
};
List<Sample> list;
if (!FinalList.TryGetValue(NewItem.IdOfSample, out list))
{
list = new List<Sample>();
FinalList.Add(NewItem.IdOfSample, list);
}
list.Add(NewItem);
TryGetValue will see if the dictionary has an entry for the key you pass it and returns true if it does and false if it does not. If it does have an entry for the key it also assigns the value of the entry (in this case your list of samples) to the out parameter. So, we check if it returns false and in that case we create a new list and add it to the dictionary. Then we add the sample to the list that we either got from the dictionary, or just created and put in the dictionary.
I'm trying to retrieve a dataset from RavenDB, group it by several categories and then store it per group in a dictionary. The retrieval and groupby have been solved. However, I'm stuck on how to put the combined group (group on 4 variables) into a dictionary. So in other words: the dictionary needs to be filled with each distinct name/year/month/day combination. I need this to later on display it in a graph - that part is already covered.
Dictionary<string, int> chartInformation = new Dictionary<string, int>();
List<string> xAxisCategories = new List<string>();
if (model.Period.Value == Timespan.Day)
{
var groupedRecords = transformedRecords.GroupBy(x => new
{
x.Name,
x.DateTo.Value.Year,
x.DateTo.Value.Month,
x.DateTo.Value.Day
});
foreach (var recordGroup in groupedRecords)
{
if (!chartInformation.ContainsKey(recordGroup.Key.Name,
recordGroup.Key.Year, recordGroup.Key.Month, recordGroup.Key.Day))
// how to do this properly
{
chartInformation.Add(recordGroup.Key.?, 0);
}
if (!xAxisCategories.Contains(recordGroup.Key.?))
{
xAxisCategories.Add(recordGroup.Key.?);
}
foreach (var record in recordGroup)
{
//filling stuff here
}
}
}
You need to project your group key to a string like this:
var groupedRecords = transformedRecords.GroupBy(x => String.Format("{0}-{1}-{2}-{3}",
x.Name,
x.DateTo.Value.Year,
x.DateTo.Value.Month,
x.DateTo.Value.Day));
Then you can iterate over the groups and check for existence of a certain key in your dictionary:
foreach(var group in groupedRecords)
{
if(!chartInformation.ContainsKey(group.Key))
{
chartInformation.Add(group.Key, 0)
}
}
However, keep in mind that projecting your key to a string as shown above can lead to collisions, i.e. rows that belong to different groups may end up in the same group.
Hope this helps.
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.