Split collection linq entity framework - c#

I have a PropertyValues entity which reperesent product properties:
public enum Property { Color = 1, Brand, Size }
public class PropertyValue
{
public int PropertyValueId { get; set; }
public Property PropertyId { get; set; }
public string Value { get; set; }
public ICollection<Product> Products { get; set; }
public PropertyValue()
{
Products = new HashSet<Product>();
}
}
Cause we have end sequnce of product properties i created an enum Property which keep properties.
I'm trying to achieve following result - to split collection depending on properties. Where the values are an another Dictionary
Dictionary<Property, Dictionary<int, string>> dict = new Dictionary<Property, Dictionary<int, string>>()
{
new KeyValuePair<Property, Dictionary<int, string>>()
{
{
Property.Color,
new KeyValuePair<int, string>()
{
{ 5, "white" }, // ProperyValueId, Value
{ 6, "green"}
}
}
}
}
I have been looked aside GroupBy and SelectMany, but didn't find a way.
For now i have following:
var query = await db.PropertyValues
.OrderBy(prop => prop.PropertyId)
.Where(prop => prop.PropertyId == Property.Brand)
.ToDictionaryAsync(prop => prop.PropertyValueId, prop => prop.Value);
Dictionary<Property, Dictionary<int, string>> properties = new Dictionary<Property, Dictionary<int, string>>();
properties.Add(Property.Brand, query);
Should return a json. But firstly need to get sequence. Json should look like:
[{
{"colors": [{"5", "Black"}, {"7", "White"}]},
{"brands": [{"78", "Q&Q"}, {"24", "Adidas"}]},
}]

The first GroupBy splits the list of PropertyValues by PropertyId, then this grouping is converted to a dictionary that has PropertyId as Key.
The values of each record of our dictionary is composed by creating a new dictionary where the key is PropertyValueId and the value is Value
PropertyValues.GroupBy( k => k.PropertyId )
.ToDictionary( k => k.First().Value,
k => k.ToDictionary( p => p.PropertyValueId, p => p.Value ) );
Now data is structured as you want.

Related

Dictionary out of a complex nested object

I have DTOs like this
public class Obj1
{
public string a1 { get; set; };
public Obj2[] a2 { get; set; };
}
public class Obj2
{
public string b1 { get; set; };
public Obj3[] b2 { get; set; };
}
public class Obj3
{
public string Key { get; set; };
}
So, the object will be like
Obj1 o = new Obj1
{
a1="a";
a2=new[]
{
new Obj2
{
b1="b";
b2=new[]
{
new Obj3
{
Key="c";
}
}
}
}
}
I have Obj1. How can I group it in the form of dictionary of type
IDictionary<string, IEnumerable<Obj2>>
where Key is in Obj3.
I tried using GroupBy but did not get the relevant result.
If I understand the end goal - co combine all Obj2 by all child Obj3.Key and after fixing the code to make it compile you can use SelectMany into intermediate data structure with aggregation with GroupBy:
var dictionary = o.a2
.SelectMany(o2 => o2.b3.Select(o3 => (o3.Key, o2)))
.GroupBy(t => t.Key)
.ToDictionary(g => g.Key, g => g.ToList());
Im not quite sure, but your class "obj2" instead of having 2 properties, you can make it with one property of Dictionary with a string key and Ienumerable value, so your variable could look like this.
Dictionary<string, Dictionary<string, Ienumerable>> and you can add "obj3" as a value or key.

Finding differences between 3 complicated dictionaries in C#

I am having trouble juggling 3 dictionaries to find differences such as missing entries and also property values.
The dictionaries key and values are these objects:
public class Car
{
public string CarBrand{ get; set; }
public string RentingCompany{ get; set; }
public string CompanyPhone{ get; set; }
public ComplexObj MyObj { get; set; } //This is a deserialized XML
}
public class Drivers
{
public string DriverID{ get; set; } // Unique
public string Name{ get; set; }
public string LastName{ get; set; }
}
public Dictionary<Drivers, List<Car>> List1 = new Dictionary<Drivers, List<Car>>();
public Dictionary<Drivers, List<Car>> List2 = new Dictionary<Drivers, List<Car>>();
public Dictionary<Drivers, List<Car>> List3 = new Dictionary<Drivers, List<Car>>();
I need to search in List1.Values all the CarBrands that are not in list2 or List3 and the save the entire KeyValue pair Driver and Value into a new dictionary.
I would gladly accept any guidance on what would be an optimal way to approach this.
Thanks
This approach is going to be much much faster than comparing every car brand in list1 with every car brand in list2 and list3. The approach shown in the other answer has high computational complexity, it would scale badly for large amounts of data.
I haven't really tested it, but I think it is correct.
// only the distinct car brands throughout list2 and list3 remain in this hash set
HashSet<string> carBrandsInlist2and3 = new(
List2.Values
.Concat(List3.Values)
.SelectMany(cars => cars.Select(car => car.CarBrand)));
// using a hashset for IsSubsetOf below is going to be much quicker than
// using a 'simple' IEnumerable because set operations have optimizations in place in case
// both sets are of the same type and have the same IEqualityComparer
HashSet<string> tempHashset = new();
Dictionary<Drivers, List<Car>> newDictionary = new(
List1
.Where(keyValuePair =>
{
// fill the temporary hashset with car brands of this kvp
tempHashset.Clear();
tempHashset.UnionWith(keyValuePair.Value.Select(car => car.CarBrand));
// if tempHashset is not a subset of carBrandsInlist2and3
// then in this keyValuePair there is a car brand that does not exist in list2 or list3
return !tempHashset.IsSubsetOf(carBrandsInlist2and3);
}));
var brands2 = List2.Values.SelectMany(v => v.Select(c => c.CarBrand));
var brands3 = List3.Values.SelectMany(v => v.Select(c => c.CarBrand));
var allBrands = brands2.Concat(brands3);
var keyValuePairsToSave = List1
.Where(pair => !pair.Value.Any(car => allBrands.Any(brand => brand == car.CarBrand)))
// or
//.Where(pair => !allBrands.Any(brand => pair.Value.Any(c => c.CarBrand == brand)))
.ToArray();
//or
//.ToDictionary(pair => pair.Key, pair => pair.Value);

How to turn dapper result into a dictionary using result mapping

I want to use the splitOn feature denoted here: https://dapper-tutorial.net/result-multi-mapping
to group every Order of the results to a integer property "EmployeeId". I Followed the advice from How to map to a Dictionary object from database results using Dapper Dot Net?
but I am getting a An item with the same key has already been added. so how can I group my orders by EmployeeId?
I cannot modify the Order class and I prefer using a dictionary over creating a class that wraps Order. However, if there is no other way I am open to the idea of wrapping Order
https://dotnetfiddle.net/hn6Sjf
public class Program
{
public class Order
{
public int OrderID { get; set; }
public int CustomerID { get; set; }
public DateTime OrderDate { get; set; }
public int ShipperID { get; set; }
}
public static void Main()
{
string sql = #"
SELECT TOP 10
EmployeeID,
OrderID,
CustomerID,
OrderDate,
ShipperID
FROM Orders
ORDER BY OrderID;
";
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
var rawList = connection.Query<Order>(sql);
FiddleHelper.WriteTable(rawList);
var dict = connection.Query<int, List<Order>, KeyValuePair<int, List<Order>>>(sql,
(s, i) => new KeyValuePair<int, List<Order>>(s, i), null, null, true, "OrderID")
.ToDictionary(kv => kv.Key, kv => kv.Value);
FiddleHelper.WriteTable(dict);
}
}
}
Would this meet your needs?
var dict = connection.Query<int, Order, ValueTuple<int, Order>>(sql,
(s, i) => ValueTuple.Create(s, i), null, null, true, "OrderID")
.GroupBy(t => t.Item1, t => t.Item2, (k, v) => new {Key = k, List = v})
.ToDictionary(kv => kv.Key, kv => kv.List);
Fiddle
You could create an envelope class (Or use dynamic if you prefer that):
public class OrderEntity
{
public int EmployeeID {get;set;}
public Order Order {get;set;}
}
And then the mapping from the resultset into a dictionary grouped by employee id is straight forward:
var dict = new Dictionary<int,List<Order>>();
var r = connection.Query<OrderEntity, Order, OrderEntity>(sql,(orderEntity, order) =>
{
// You can skip that line if you want, the orderEntity is (probably) never used.
orderEntity.Order = order;
if(dict.ContainsKey(orderEntity.EmployeeID))
{
dict[orderEntity.EmployeeID].Add(orderEntity.Order);
}
else
{
dict.Add(orderEntity.EmployeeID, new List<Order> {orderEntity.Order});
}
return orderEntity;
}, splitOn: "OrderID");
This method does it all in 1 iteration over the result set and only requires a O(1) key lookup into the dictionary.

LINQ: Getting item from dictionary by key and also its values then assign them to variable

In Parts class we have Data dictionary that contains key "Number" and value "1" for example. The key is always called "Number" and the value is always string of some number 1,2,3 etc. I want to assign to one variable (List) all items that has the key "number" with their values and then to group them by the id in Parts. So in the end the result should be the Id from Parts, Number and its value.
public class People
{
public List<Parts> Parts { get; set; }
}
public class Parts
{
public string Name {get;set;}
public string Id {get;set;}
public Dictionary<string,string> Data {get;set}
}
var msf = new People();
Currently my example that does not work properly with linq :
var temp = msf
.Parts
.Select(s => s.Data.Keys.Where(key => key.Contains("Number"))
.ToList()
.Select(s = > s.Value));
Can someone give me better solution for this scenario code with linq?
"People":[
"id":"1234567"
"Parts":[
"id":"234567",
"name":"Lqlq"
"Data":{
"number" : "1"
}
"id":"3424242",
"name":"Lqlq2"
"Data":{
"number" : "2"
}
]
]
This should give you a Dictionary<string, List<string>> containing a list of ID strings for each "Number" value:
var idsByNumber = msf.Parts.Where(p => p.Data.ContainsKey("number")) // filter for all that have a number
.Select(p => new { ID = p.ID, Number = p.Data["number"] }) // select ID and the number value
.GroupBy(x => x.Number) // group by number
.ToDictionary(g => g.Key, g => g.ToList()); // create dictionary number -> id list
Here's an alternative syntax.
var temp = from part in msf.Parts
where part.Data["Number"] == "2"
select part;
Usually is a good idea to ask your questions using an MCVE - here's some code that can be pasted in Linqpad:
void Main()
{
var msf = new People() {
Parts = new List<Parts> {
new Parts { Name = "Lqlq", Id = "234567", Data = new Dictionary<string, string> { { "Number", "1"} } },
new Parts { Name = "Lqlq2", Id = "3424242", Data = new Dictionary<string, string> { { "Number", "2"} } },
}
};
var temp = from part in msf.Parts
where part.Data["Number"] == "2"
select part
;
temp.Dump();
}
public class People
{
public List<Parts> Parts { get; set; }
}
public class Parts
{
public string Name { get; set; }
public string Id { get; set; }
public Dictionary<string, string> Data { get; set; }
}

Convert List<T> to Dictionary with strategy

I have List < DTO > where DTO class looks like this,
private class DTO
{
public string Name { get; set; }
public int Count { get; set; }
}
I create objects and add it to List.
var dto1 = new DTO { Name = "test", Count = 2 };
var dto2 = new DTO { Name = "test", Count = 3 };
var dtoCollection = new List<DTO> {dto1, dto2};
Now my requirement is I need to create an Dictionary from the dtoCollection whose Key is Name field and value is Sum of Count fields.
For example, if you convert the above dtoCollection to Dictionary, the entry should be like
Key = "test" , Value = "5"
Where Value is obtained from summing up the Count fields whose Name fields are same
I guess this will do what you need:
dtoCollection.GroupBy(x => x.Name).ToDictionary(x => x.Key, x => x.Sum(y => y.Count))
Following is the linq query
var dict = dtoCollection.GroupBy(x=>x.Name).ToDictionary(x=>x.Key, x=>x.Select(y=>y.Count).Sum());

Categories