Dynamic Linq Groupby SELECT Key, List<T> - c#

I am using Dynamic Linq helper for grouping data. My code is as follows :
Employee[] empList = new Employee[6];
empList[0] = new Employee() { Name = "CA", State = "A", Department = "xyz" };
empList[1] = new Employee() { Name = "ZP", State = "B", Department = "xyz" };
empList[2] = new Employee() { Name = "AC", State = "B", Department = "xyz" };
empList[3] = new Employee() { Name = "AA", State = "A", Department = "xyz" };
empList[4] = new Employee() { Name = "A2", State = "A", Department = "pqr" };
empList[5] = new Employee() { Name = "BA", State = "B", Department = "pqr" };
var empqueryable = empList.AsQueryable();
var dynamiclinqquery = DynamicQueryable.GroupBy(empqueryable, "new (State, Department)", "it");
How can I get back the Key and corresponding list of grouped items i.e IEnumerable of {Key, List} from dynamiclinqquery ?

I solved the problem by defining a selector that projects the Key as well as Employees List.
var eq = empqueryable.GroupBy("new (State, Department)", "it").Select("new(it.Key as Key, it as Employees)");
var keyEmplist = (from dynamic dat in eq select dat).ToList();
foreach (var group in keyEmplist)
{
var key = group.Key;
var elist = group.Employees;
foreach (var emp in elist)
{
}
}

The GroupBy method should still return something that implements IEnumerable<IGrouping<TKey, TElement>>.
While you might not be able to actually cast it (I'm assuming it's dynamic), you can certainly still make calls on it, like so:
foreach (var group in dynamiclinqquery)
{
// Print out the key.
Console.WriteLine("Key: {0}", group.Key);
// Write the items.
foreach (var item in group)
{
Console.WriteLine("Item: {0}", item);
}
}

Related

How do I update Dictionary values (object) irrespective of key

I have a dictionary which has more than 2000+ elements. I am trying to update an element in the value based on a condition. How do I do that in a simple statement instead of looping through?
Dictionary<int, MyObj> testDictionary = new Dictionary<int, MyObj>();
testDictionary.Add(1,new MyObj() {Name = "John",Code = "JN",Id = null});
testDictionary.Add(2,new MyObj() {Name = "John",Code = "JN",Id = null});
testDictionary.Add(3,new MyObj() {Name = "Champ",Code = "CP",Id = null});
testDictionary.Add(4,new MyObj() {Name = "SMITH",Code = "SH",Id = null});
testDictionary.Add(5,new MyObj() {Name = "SMITH",Code = "SH",Id = null});
Dictionary<int, MyObj>
public class MyObj
{
public string Name { get; set; }
public string Code { get; set; }
public int? Id { get; set; }
}
How do I update all of Dictionary values (MyObj)
where Code = JN and Name = John with 100
where Code = SH and Name = SMITH with 989
I can get matched list using below statement but do I ressign to dictionary?
var enList = testDictionary.Values.Where(d => d.Name == "John" && d.Code == "JN");
Try the code bellow, this change the value of name to test attribute in any item where the value of Name is "SMITH".
testDictionary.Where(y => y.Value.Name == "SMITH")
.Select(r => r.Value)
.ToList<MyObj>()
.ForEach(k => k.Name = "test");
This line does not return result, the value is changed directly in the textDictionary instance.
Look at the example - you don't need to reassign values in the Dictionary, they are stored as references.
var enList = testDictionary.Values.Where(d => d.Name == "John" && d.Code == "JN").ToList();
foreach (var item in enList)
{
item.Name = "Updated Name";
}
Console.WriteLine(testDictionary[1].Name);

How to choose certain values from ICollection to IDictionary, using LINQ

I have IDictionary<int,bool?> where int - id, bool? - state (true,false,null)
So i need to filter ICollection of objects, where i should compare internal id with id of my IDictionary and if IDs are the same and the state is true - i should select this element (using LINQ)
I tried: incomeCollection.Values.Select(x=>x.InternalId.Equals(dataFromDictionary.Keys.Any)).Select(h=> new Item){Item = h.Name}
but it does not works. I need to check all collection and select elements, which satisfy the condition above using LINQ. How can i do this?
The dictionary has a handy TryGetValue method allowing to look up an entry quickly.
class Income
{
public int InternalId { get; set; }
public string Name { get; set; }
}
var dictionary = new Dictionary<int,bool?>
{
{1, false},
{2, true},
{3, null},
};
var incomeCollection = new List<Income>
{
new Income { InternalId = 1, Name = "A" },
new Income { InternalId = 2, Name = "B" },
new Income { InternalId = 3, Name = "C" },
new Income { InternalId = 4, Name = "D" },
};
var result = incomeCollection.Where(x =>
dictionary.TryGetValue(x.InternalId, out var status) && status == true)
.Select(h=> new {Item = h.Name});
This is better than your first approach using dataFromDictionary.Keys.Any which does not take advantage of the Dictionary feature of quick lookup.
You can use Any()
var result = list.Where(x=>dictionary.Keys.Any(c=>c.Equals(x.Id)) && x.State.HasValue && x.State==true).Select(x=>x);
You can also use Join.
var result = list.Join(dictionary,
l=>l.Id,
d=>d.Key,(l,d)=>l)
.Where(x=>x.State.HasValue && x.State==true).Select(x=>x);
For example,
var dictionary = new Dictionary<int,bool?>
{
[1] = true,
[3] = true,
[35] = false
};
var list = new List<Person>
{
new Person{Name="Jia", Id=1,State=false},
new Person{Name="Aami", Id=3,State=true},
new Person{Name="Anu", Id=35,State=null},
};
Output
Aami 3 True
ToDictionary method can be used as shown in the below example.
In order to select only few values from ICollection you can use where clause.
List<Package> packages =
new List<Package>
{ new Package { Company = "Coho Vineyard", Weight = 25.2, TrackingNumber = 89453312L },
new Package { Company = "Lucerne Publishing", Weight = 18.7, TrackingNumber = 89112755L },
new Package { Company = "Wingtip Toys", Weight = 6.0, TrackingNumber = 299456122L },
new Package { Company = "Adventure Works", Weight = 33.8, TrackingNumber = 4665518773L } };
// Create a Dictionary of Package objects,
// using TrackingNumber as the key.
Dictionary<long, Package> dictionary =
packages.ToDictionary(p => p.TrackingNumber);
foreach (KeyValuePair<long, Package> kvp in dictionary)
{
Console.WriteLine(
"Key {0}: {1}, {2} pounds",
kvp.Key,
kvp.Value.Company,
kvp.Value.Weight);
}

How to get the value of a specific properties parameter in a collection of Custom object using multiple where clause in LINQ?

I have the following list:
List<Product> productList = new List<Product>
{
new Product(){ Name = "C", OriginYear=1992,DevelopementYear=1992,IValue=110},
new Product(){ Name = "C", OriginYear=1992,DevelopementYear=1993,IValue=170},
new Product(){ Name = "C", OriginYear=1993,DevelopementYear=1993,IValue=200},
new Product(){ Name = "NC", OriginYear=1990,DevelopementYear=1990,IValue=45.2M},
new Product(){ Name = "NC", OriginYear=1990,DevelopementYear=1991,IValue=64.8M},
new Product(){ Name = "NC", OriginYear=1990,DevelopementYear=1993,IValue=37.0M},
new Product(){ Name = "NC", OriginYear=1991,DevelopementYear=1991,IValue=50},
new Product(){ Name = "NC", OriginYear=1991,DevelopementYear=1992,IValue=75},
new Product(){ Name = "NC", OriginYear=1991,DevelopementYear=1993,IValue=25},
new Product(){ Name = "NC", OriginYear=1992,DevelopementYear=1992,IValue=55},
new Product(){ Name = "NC", OriginYear=1992,DevelopementYear=1993,IValue=85},
new Product(){ Name = "NC", OriginYear=1993,DevelopementYear=1993,IValue=100}
};
Now I want to get the IValue of the Product which is having OriginYear=1992 and DevelopementYear=1992.
This is my query:
var temp = productList.Where(p => p.Name == "C").Where(p => p.OriginYear == 1992).Where(p => p.DevelopementYear == 1992).Select(p=>p.IValue);
Its returning 0.
However, if I check:
new System.Linq.SystemCore_EnumerableDebugView<decimal>(temp).Items[0]
It is giving desired value.(110)
May I know whats going on?How can I improve the query?
Thanks in advance.
Select returns an IEnumerable<T> and you can iterate through it by using foreach
var temp = productList.Where(p => p.Name == "C" && p.OriginYear == 1992 && p.DevelopementYear == 1992).Select(p => p.IValue);
foreach(var t in temp)
{
Console.WriteLine(t);
}
Or you can create a List or an array object out of it if you want to reach an individual element by using an index value
var temp = productList.Where(p => p.Name == "C" && p.OriginYear == 1992 && p.DevelopementYear == 1992).Select(p => p.IValue).ToArray();
Console.WriteLine(temp[0]);

List order by another contained list

I have a list of Objects and one of the item is another list. How can I Group them based on the inner list.
Here is an example of what I wish to do.
class Student
{
public string Name;
public int Age;
public List<GroupInfo> GroupList; // This is the inner list
}
class GroupInfo
{
public string GroupName;
public int GroupId;
}
static void Main()
{
GroupInfo firstGroup = new GroupInfo
{
GroupId = 1,
GroupName = "First group"
};
GroupInfo secondGroup = new GroupInfo
{
GroupId = 2,
GroupName = "Second group"
};
GroupInfo thirdGroup = new GroupInfo
{
GroupId = 3,
GroupName = "Third group"
};
GroupInfo fourthGroup = new GroupInfo
{
GroupId = 4,
GroupName = "Fourth group"
};
List<Student> studentList = new List<Student>();
Student firstStudent = new Student();
firstStudent.Name = "Name1";
firstStudent.Age = 15;
firstStudent.GroupList = new List<GroupInfo>();
firstStudent.GroupList.Add(firstGroup);
firstStudent.GroupList.Add(secondGroup);
studentList.Add(firstStudent);
Student secondStudent = new Student();
secondStudent.Name = "Name2";
secondStudent.Age = 17;
secondStudent.GroupList = new List<GroupInfo>();
secondStudent.GroupList.Add(firstGroup);
secondStudent.GroupList.Add(thirdGroup);
studentList.Add(secondStudent);
Student thirdStudent = new Student();
thirdStudent.Name = "Name3";
thirdStudent.Age = 18;
thirdStudent.GroupList = new List<GroupInfo>();
thirdStudent.GroupList.Add(secondGroup);
thirdStudent.GroupList.Add(thirdGroup);
thirdStudent.GroupList.Add(fourthGroup);
studentList.Add(thirdStudent);
List<GroupInfo> groupInfoList = new List<GroupInfo>();
// Now What I want is to get a group List Where...
foreach (var student in studentList)
{
// ...First Group Should contains firstStudent and secondStudent
// Second group Should firstStudent & thirdStudent
// Third group Should contains secondStudent & thirdStuden
// Fourth Group Should contains only thirdStudent
}
}
One way is to iterate on the whole List and populate the GroupInfo List. Just wondering is there any other way to do this task.
You can do this with SelectMany like this:-
var result = studentList.SelectMany(x => x.GroupList,
(studentObj, groups) => new { studentObj, groups })
.GroupBy(x => new { x.groups.GroupId, x.groups.GroupName })
.Select(x => new
{
GroupId = x.Key.GroupId,
GroupName = x.Key.GroupName,
Students = x.Select(z => z.studentObj).ToList()
});
Since your GroupInfo class only has two properties i.e. GroupId & GroupName, you won't be able to fetch the Students associated with it. This is the reason I am fetching anonymous type out of it.
I am getting following output with this query:-

Combining two Lists of an Object into one

I am currently developing an application that requires this senario.
Assuming I have this object
public class ObjectItem
{
public int Id {get;set;}
public int Name {get;set;}
public int Sex {get;set;}
public int Age {get;set;}
public string Complexion {get;set;}
}
If we now have two lists of this object
var studentWithAge = new List<ObjectItem>
{
new ObjectItem {Id = 1, Name = "John", Age = 2},
new ObjectItem {Id = 2, Name = "Smith", Age = 5},
new ObjectItem {Id = 3, Name = "Juliet", Age = 7},
};
var studentWithSexAndComplexion = new List<ObjectItem>
{
new ObjectItem {Id = 1, Name = "John", Sex = "Male", Complexion = "fair"},
new ObjectItem {Id = 2, Name = "Smith", Sex = "Male", Complexion = " "},
new ObjectItem {Id = 3, Name = "Juliet", Sex = "Female", Complexion = "Blonde"},
new ObjectItem {Id = 4, Name = "Shittu", Sex = "Male", Complexion = "fair"},
};
I want to merge these two lists into just one. The end result should look like this.
var CompleteStudentData=new List<ObjectItem>
{
new ObjectItem{Id=1,Name="John",Sex="Male", Complexion="fair",Age=2},
new ObjectItem{Id=2,Name="Smith",Sex="Male", Complexion=" ", Age=5},
new ObjectItem{Id=3,Name="Juliet",Sex="Female", Complexion="Blonde", Age=7},
new ObjectItem{Id=4,Name="Shittu",Sex="Male", Complexion="fair", Age=0},
}
How do i achieve this? Using Union to merge the two list does not produce the desired result. I would appreciate your help.
var result = StudentWithAge.Join(StudentWithSexAndComplexion,
sa => sa.Id,
ssc => ssc.Id,
(sa, ssc) => new ObjectItem
{
Id = sa.Id,
Name = sa.Name,
Age = sa.Age,
Sex = ssc.Sex,
Complexion = ssc.Complexion
}).ToList();
Or, avoiding creation of new objects:
var result = StudentWithAge.Join(StudentWithSexAndComplexion,
sa => sa.Id,
ssc => ssc.Id,
(sa, ssc) =>
{
sa.Sex = ssc.Sex;
sa.Complexion = ssc.Complexion;
return sa;
}).ToList();
And if you want to add students presented only in the second list, than also:
result.AddRange(StudentWithSexAndComplexion.Where(ssc => !StudentWithAge.Any(sa => sa.Id == ssc.Id)));
Since it's possible that your collections will not have a 1-to-1 correspondence, you would have to do a full outer join. See here for how you can compose it that way.
Here's one way you can get similar results.
Collect all the keys (the ids) from both collections, then perform a left join with each of the collections, then combine the results.
var ids = studentWithAge.Select(s => s.Id)
.Union(studentWithSexAndComplexion.Select(s => s.Id));
var query =
from id in ids
from sa in studentWithAge
.Where(sa => sa.Id == id)
.DefaultIfEmpty(new ObjectItem { Id = id })
from ssc in studentWithSexAndComplexion
.Where(ssc => ssc.Id == id)
.DefaultIfEmpty(new ObjectItem { Id = id })
select new ObjectItem
{
Id = id,
Name = sa.Name ?? ssc.Name,
Sex = ssc.Sex,
Age = sa.Age,
Complexion = ssc.Complexion,
};
.Net has a function which is concatenating collections:
var concatenatedCollection = StudentWithAge.Concat(StudentWithSexAndComplexion).ToList();
var StudentWithAge = new List<ObjectItem>()
{
new ObjectItem{Id=1,Name="John",Age=2},
new ObjectItem{Id=2,Name="Smith",Age=5},
new ObjectItem{Id=3,Name="Juliet",Age=7},
};
var StudentWithSexAndComplexion = new List<ObjectItem>()
{
new ObjectItem{Id=1,Name="John",Sex="Male", Complexion="fair"},
new ObjectItem{Id=2,Name="Smith",Sex="Male", Complexion=" "},
new ObjectItem{Id=3,Name="Juliet",Sex="Female", Complexion="Blonde"},
new ObjectItem{Id=4,Name="Shittu",Sex="Male", Complexion="fair"},
};
var concatenatedCollection = StudentWithAge.Concat(StudentWithSexAndComplexion).ToList();

Categories

Resources