LINQ - List to Dictionary, group by multiple properties - linq

I have a HashSet of 2000 items like this (the Index is not a property of the item):
Index LocationId UserId OtherProperty
1 1 1 abc
2 1 2 zyx
3 1 3 nvme
4 1 4 pcie
5 2 1 test
6 2 2 temp
7 2 3 etc
8 2 4 hah
...........................................
2000 500 4 last
If I do:
hashSet.GroupBy(o => o.LocationId)
.ToDictionary(g => g.Key, g => g.ToList());
I get a dictionary with 500 locations, each containing a list of 4 users.
But I need a dictionary of 2000 items, where the other properties are preserved, each having a list of 4 users. How do I do that?

Your question is not clear. I did a wild guess what you're trying and set up some examples for you:
static void Main(string[] args)
{
List<MyClass> list = new List<MyClass>()
{
new MyClass() { Index = 1, LocationId = 1, UserId = 1, OtherProperty = "abc" },
new MyClass() { Index = 2, LocationId = 1, UserId = 2, OtherProperty = "qwe" },
new MyClass() { Index = 3, LocationId = 1, UserId = 3, OtherProperty = "asd" },
new MyClass() { Index = 4, LocationId = 1, UserId = 1, OtherProperty = "yxc" },
new MyClass() { Index = 5, LocationId = 2, UserId = 2, OtherProperty = "acb" },
new MyClass() { Index = 6, LocationId = 2, UserId = 3, OtherProperty = "ghj" },
new MyClass() { Index = 7, LocationId = 2, UserId = 1, OtherProperty = "uio" },
new MyClass() { Index = 8, LocationId = 2, UserId = 2, OtherProperty = "zhn" }
};
// Index is Unique => We build a Dictionary with one object per index.
Dictionary<int, MyClass> dicIndexToObject = list.ToDictionary(d => d.Index);
// We got multiple objects per Locations => We need a Lookup
ILookup<int, MyClass> lookupLocationToObject = list.ToLookup(l => l.LocationId);
// If we ask with a key, we get a List:
IEnumerable<MyClass> tmp = lookupLocationToObject[1];
Console.WriteLine("--- What do we have per LocationId? ---");
foreach (MyClass obj1 in tmp)
{
Console.WriteLine(obj1.ToString());
}
ILookup<int, MyClass> lookupUserIdToObject = list.ToLookup(l => l.UserId);
Console.WriteLine("--- What do we have per UserId? ---");
foreach (MyClass obj2 in lookupUserIdToObject[1])
{
Console.WriteLine(obj2.ToString());
}
// What if you want to get deeper?
// What if we want to search for an index within a Location?
Dictionary<int, Dictionary<int, MyClass>> dicLocationToIndexToObject =
list.GroupBy(l => l.LocationId) // We group by location
.ToDictionary(g => g.Key, values => values.ToDictionary(k => k.Index)); // We form the grouping to a dictionary. Instead of accepting a List as value, we form another dictionary
// if we now want to search for a index in a specific location we do this:
Console.WriteLine("--- Let's get out object for Index '1' in location '1' ---");
Dictionary<int, MyClass> tmp2 = dicLocationToIndexToObject[1];
MyClass obj = tmp2[1];
Console.WriteLine(obj);
Console.WriteLine("--- Lets get all objects for UserId '1' in location '2' ---");
Dictionary<int, ILookup<int, MyClass>> dicLocationToUserIdLookup =
list.GroupBy(l => l.LocationId) // We group by location
.ToDictionary(g => g.Key, values => values.ToLookup(k => k.UserId));
foreach (MyClass obj3 in dicLocationToUserIdLookup[2][1])
{
Console.WriteLine(obj3.ToString());
}
}
public class MyClass
{
public int Index { get; set; }
public int LocationId { get; set; }
public int UserId { get; set; }
public string OtherProperty { get; set; }
public override string ToString()
{
return String.Format(" Index: {0}, LocationId: {1}, UserId: {2}, OtherProperty: {3}", this.Index, this.LocationId, this.UserId, this.OtherProperty);
}
}

Related

How to write a Linq that can retrieve all parent table records and total of sub-table record, I mean 'separate' into two parts

Let's say I have two tables, parent table 'P' and sub-table 'S', I usually wrote the Linq like this to get what I want:
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select new{
Id = p.Id,
Title = p.Title
SubContentCount = subContent.Count()
}
It's very simple, but if for some reason I have to pass a parameter into this query when there has one (let's say 'key'), I have to do this (I guess :-):
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select p;
if(!string.IsNullOrEmpty(key)){ // I'm using C#
rows = rows.Where(q => q.Title.Contains(key))
}
And then:
var list = rows.Select(q => new ()
{
Id = q.Id,
Title = q.Title,
subCount = ???.Count()
});
Is that passable to do Linq like this? if so, how?
Thanks for any kind help!
You could create a method that receives a Func<Table, bool>as parameter and use it to filter your dataset:
public static void Main(string[] args)
{
var rows = new List<Table>
{
new Table { Id = 1, Title = "A", SubContent = new [] { "A1" } },
new Table { Id = 2, Title = "B", SubContent = new [] { "B1", "B2" } },
new Table { Id = 3, Title = "C", SubContent = new [] { "C1", "C2", "C3" } },
};
var title = "C";
foreach (var item in Filter(rows, table =>
String.IsNullOrEmpty(title) || table.Title == title))
{
Console.WriteLine(
"Title={0}, SubContent.Length={1}",
item.Title, item.SubContent.Length);
}
}
public static List<Table> Filter(List<Table> original, Func<Table, bool> filter)
{
return original.Where(filter).ToList();
}
public class Table
{
public int Id { get; set; }
public string Title { get; set; }
public string[] SubContent { get; set; }
}
Why not include the filter in the where clause?
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
Quick example in the interactive console:
public class Parent { public int Id {get; set;} public string Title {get; set;} }
public class SubTable { public int Id {get; set;} public int ParentId {get; set;} }
public class Result { public int Id {get; set;} public string Title {get; set;} public int SubContentCount {get; set;} }
var p1 = new Parent() { Id = 1, Title = "Parent_1" };
var p2 = new Parent() { Id = 2, Title = "Parent_2" };
var p3 = new Parent() { Id = 3, Title = "Parent_3" };
var s1_1 = new SubTable() { Id = 11, ParentId = 1 };
var s1_2 = new SubTable() { Id = 12, ParentId = 1 };
var s1_3 = new SubTable() { Id = 13, ParentId = 1 };
var s2_1 = new SubTable() { Id = 21, ParentId = 2 };
var s2_2 = new SubTable() { Id = 22, ParentId = 2 };
var s3_1 = new SubTable() { Id = 31, ParentId = 3 };
var db_P = new List<Parent>() { p1, p2, p3 };
var db_S = new List<SubTable>() { s1_1, s1_2, s1_3, s2_1, s2_2, s3_1 };
public IEnumerable<Result> GetResults(string key = null)
{
var rows = from p in db_P
join s in db_S on p.Id equals s.ParentId into subContent
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
select new Result() {
Id = p.Id,
Title = p.Title,
SubContentCount = subContent.Count()
};
return rows;
}
And example output (formatted onto multiple lines for readability)
> GetResults().ToList()
List<Submission#0.Result>(3) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" },
Submission#0.Result { Id=2, SubContentCount=2, Title="Parent_2" },
Submission#0.Result { Id=3, SubContentCount=1, Title="Parent_3" }
}
> GetResults("1").ToList()
List<Submission#0.Result>(1) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" }
}
>

linq query to sort and then bring a particular group of records to top

i am trying a linq query to sort a group of elements and then bring a elements that satisfy particular condition to top.
for eg, if i have list of elements like below:
ID Names
1 Angie
2 Bret
3 Salva
4 cunnighma
5 maria
6 Galvin
7 Newton
8 Desmond
and if i pass condition as Name=Galvin then the resultset should be sorted first and then bring the value inn condition to top.The resultset would like below
ID Names
6 Galvin
1 Angie
2 Bret
4 cunnighma
8 Desmond
5 maria
7 Newton
3 Salva
One option is to create an extension method that can be used in your linq expression. The following solution does just that. I've made it work for the case of multiple matches as well.
The example code below will have the following output:
6:Galvin
1:Angie
2:Bret
4:cunnighma
8:Desmond
5:maria
7:Newton
3:Salva
Here's the code:
void Main()
{
// Setup the example data
var names = new List<Record>
{
new Record { Id = 1, Name = "Angie" },
new Record { Id = 2, Name = "Bret" },
new Record { Id = 3, Name = "Salva" },
new Record { Id = 4, Name = "cunnighma" },
new Record { Id = 5, Name = "maria" },
new Record { Id = 6, Name = "Galvin" },
new Record { Id = 7, Name = "Newton" },
new Record { Id = 8, Name = "Desmond" }
};
// Sort the list and move the matches to the top
var result = names
.OrderBy(x => x.Name)
.MoveToTop(x => x.Name.Contains("alvin"));
// Display the results to the console
result
.ToList()
.ForEach(x => Console.WriteLine($"{x.Id}:{x.Name}"));
}
public class Record
{
public int Id { get; set; }
public string Name { get; set; }
}
public static class EnumerableExtensions
{
public static IEnumerable<T> MoveToTop<T>(this IEnumerable<T> list, Func<T, bool> predicate)
{
var matches = list.Where(predicate);
return matches.Concat(list.Except(matches));
}
}

Bitwise operation with grouping using Linq

I have a data like
public class PermList
{
public int UserId { get; set; }
public int GroupId { get; set; }
public int ModuleId { get; set; }
public int BitMaskedPermission { get; set; }
public List<PermList> TestData()
{
List<PermList> theList = new List<PermList>();
PermList sample1 = new PermList {BitMaskedPermission = 15, GroupId = 3, ModuleId = 2, UserId = 1};
theList.Add(sample1);
PermList sample2 = new PermList { BitMaskedPermission = 2, GroupId = 3, ModuleId = 1, UserId = 1 };
theList.Add(sample2);
PermList sample3 = new PermList { BitMaskedPermission = 48, GroupId = 2, ModuleId = 2, UserId = 1 };
theList.Add(sample3);
return theList;
}
}
I would like to apply OR to BitMaskedPermissions with grouping ModuleId. Here is what I would like to get;
How can I achieve this with using Linq.
TIA.
When you have an aggregation operation to perform that isn't one of the built-in ones (Sum, Max etc), you have to turn to Aggregate, which is more verbose but also much more powerful. Here, you want
var data = TestData();
var grouped =
from permList in data
group permList by new { permList.UserId, permList.ModuleId } into g
select new { // or a named class if you have one
g.Key.UserId,
g.Key.ModuleId,
BitMaskedPermission
= g.Aggregate(0, (acc, curr) => acc | curr.BitMaskedPermission)
};
Here, we pass Aggregate a function which takes the accumulator acc and the current value curr, and bitwise ORs them to get the ongoing accumulator value.
If you prefer the method-chaining syntax, it would look like (courtesy of #Chris):
var grouped = PermList.TestData()
.GroupBy(x=> new{x.UserId, x.ModuleId})
.Select(x=> new {
x.Key.UserId,
x.Key.ModuleId,
mask = x.Aggregate(0, (acc, curr)=>acc|curr.BitMaskedPermission)}
)
Maybe something like this:
PermList p=new PermList();
var result= (
from test in p.TestData()
group test by new{test.UserId,test.ModuleId} into g
select new
{
g.Key.UserId,
g.Key.ModuleId,
BitMaskedPermission= g.Sum (x =>x.BitMaskedPermission )
}
);

Linq Unique Values

i have a list of generic class which consists of 2 string property and 1 List as a property
code snipnets is as follows:
public Class abc
{
public int ID { get; set; }
public String Name { get; set; }
List<String> myList;
public List<String> Subjects
{
get
{
if (myList == null)
{
myList = new List<string>();
}
return myList;
}
}
public abc()
{
}
public abc(int id, String name, params string[] subjects)
{
Subjects.AddRange(subjects.AsEnumerable<String>());
ID = id;
Name = name;
}
}
List<abc> myList = new List<abc>();
myList.Add(new abc(1, "p1", "Maths", "Science"));
myList.Add(new abc(2, "p2", "Maths", "Art"));
myList.Add(new abc(3, "p3", "Art", "Science"));
myList.Add(new abc(4, "p4", "Geometry", "Maths"));
I need the output as
Subject Count Person
Maths 3 p1,p2,p4
Science 2 p1,p3
Art 2 p2,p3
Geometry 1 p4
Looks like you want something like:
var query = from item in myList
from subject in item.Subjects
group item.Name by subject into g
select new { Subject = g.Key,
Count = g.Count(),
Person = string.Join(",", g) };
(Change g into g.ToArray() in the string.Join call if you're using .NET 3.5.)
var result =myList.SelectMany(p => p.Subjects
.Select(q => new{Person = p.Name, Subject = q, ID = p.ID}))
.GroupBy(p => p.Subject)
.Select(p => new {Name = p.Key, Count = p.Count(), Persons = p
.Aggregate("", (a, b) => a + b.Person
+ ",").TrimEnd(',')}).OrderBy( p => p.Count);
Iterate over this collection, and print result as needed - properties of a result are Name, Count, Persons

Linq to Objects - Where search within a list

Linq to Objects - Where search within a list
internal class ProdQtyByWarehouse
{
public int id { get; set; }
public List<ProdWarehouseQty> ProdWarehouseQtys { get; set; }
}
internal class ProdWarehouseQty
{
public int id { get; set; }
public string PName { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
var list1 = new List<ProdWarehouseQty>
{
new ProdWarehouseQty
{
id = 3,
PName = "list1PN1"
},
new ProdWarehouseQty
{
id = 4,
PName = "list1PN2"
}
};
var list2 = new List<ProdWarehouseQty>
{
new ProdWarehouseQty
{
id = 5,
PName = "list2PN1"
},
new ProdWarehouseQty
{
id = 6,
PName = "list2PN2"
}
};
var prodQtyByWarehouses = new List<ProdQtyByWarehouse>
{
new ProdQtyByWarehouse {id = 1, ProdWarehouseQtys = list1},
new ProdQtyByWarehouse {id = 1, ProdWarehouseQtys = list2}
};
List<int> integers = new List<int>{2,3,4,6};
List<ProdQtyByWarehouse> list =
(from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Contains(new ProdWarehouseQty {id = 3})
select c).ToList(); // no object is returned
}
How can i achieve:
List<ProdQtyByWarehouse> list =
(from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Contains(new ProdWarehouseQty {id in integers})
select c).ToList();
List<ProdQtyByWarehouse> list =
(
from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Exists(x => integers.Contains(x.id))
select c
).ToList();

Resources