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
Related
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" }
}
>
I can use the below code to group by a ID property in an array which works.
var docArray = MyArray;
var docGroup = docArray.GroupBy(x => x.ID)
.Select(grp => new
{
Id = grp.Key,
Results = grp.ToList(),
}).ToList();
If MyArray has another array inside it which has a property say Data can some please tell me how to do the grouping based on the Data property.
class MyArray
{
SecondArray[] arr = new SecondArray[2];
public int ID{get;set;}
}
class SecondArray
{
public string Data{ get; set; }
}
var query = from a in docArray
from b in a.arr
group new { a, b } by b.Data into g
select new
{
g.Key,
Results = g.ToList()
};
I have a Complex Situation now and i am terribly stuck. Kindly Let me know if you can share some light to it.
I have a
List Which will have the Following properties
public class Categories
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
}
This will have Values like
Category1/SubCategory1
cat1/sc1
5
Category1/SubCategory2
cat1/sc2
4
Category 2/Subcategory1
cat2/sc1
5
Category 2/Subcategory2
cat2/sc2
23
I created a Custom Class to fill in the values
public class JobCateogry
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
public List<JobCateogry> SubCategories { get; set; }
}
I have to Split the String in the Code Value and assign it to the SubCategory.
Like My Final out of jobCategory would be
Category1
Cat1
9
SubCategory1
sub1
5
SubCateogry2
sub2
4
I tried to Split the string and assign it to the new class in two step first by splitting and then by assiging. But i am sure i am doing it the wrong way, because the moment i split, i loose the count .
var lstCategory = Categories
.Where(i => i.count > 0)
.Select(item => item.valueCode.Split('/')
.Select(k =>(k)).ToList();
List<JobCategories> jobcategories = lstCategory
.Select(item => item.Split(QueryStringConstants.CAT_SEPERATOR.ToCharArray()[0]))
.GroupBy(tokens => tokens[0].Trim(), tokens => tokens[1])
.Select(g => new JobCategories(g.Key, g.DisplayName,g.ToList(),)).ToList();
Can you please help?
A bit weird task
It might not be the best solution and it only works with the two layers :-), and i tried keeping a lot of linq for the fun of it
anyway hope it can get you moving forward.
full code snippet https://gist.github.com/cbpetersen/db698def9a04ebb2abbc
static void Main(string[] args)
{
var cats = new[]
{
new Categories { Count = "5", ValueCode = "cat1/sc1", DisplayName = "Category1/SubCategory1" },
new Categories { Count = "4", ValueCode = "cat1/sc2", DisplayName = "Category1/SubCategory2" },
new Categories { Count = "5", ValueCode = "cat2/sc1", DisplayName = "Category2/Subcategory1" },
new Categories { Count = "23", ValueCode = "cat2/sc2", DisplayName = "Category2/Subcategory2" }
};
var categories = cats.Select(x => x.DisplayName.Split('/')[0]).Distinct();
var list = new List<JobCateogries>();
foreach (var category in categories)
{
var a = new JobCateogries
{
ValueCode = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => x.ValueCode.Split('/')[0]).FirstOrDefault(),
DisplayName = category,
SubCategories = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => new JobCateogries
{
SubCategories = new List<JobCateogries>(),
Count = x.Count,
DisplayName = x.DisplayName.Split('/')[1],
ValueCode = x.ValueCode.Split('/')[1]
}).ToList(),
};
a.Count = a.SubCategories.Select(x => int.Parse(x.Count)).Sum().ToString();
list.Add(a);
}
list.ForEach(x => Print(x));
Console.ReadKey();
}
public static void Print(JobCateogries category, int indent = 0)
{
var prefix = string.Empty.PadLeft(indent);
Console.WriteLine(prefix + category.DisplayName);
Console.WriteLine(prefix + category.ValueCode);
Console.WriteLine(prefix + category.Count);
category.SubCategories.ForEach(x => Print(x, indent + 4));
}
I have the following table structure
ID firstName LastName zip Address
1 test1 test2 NULL NULL
2 test1 test2 12345 MI
I need to merge 2 accounts (primary & secondary) depending on IDs provided. For example, if I am given the values 1 (as primary) and 2 (as secondary) to merge.
The primary account (1) has NULL zip and Address so i need to copy those from secondary account (2) and update. The final result should be
ID firstName LastName zip Address
1 test1 test2 12345 MI
Is there any way to do using Linq or can another approach be recommended?
Though there is no native merge operator in LINQ you have a few options.
First, create your own merge!
public Account Merge(Account one, Account two) {
ret = new Account(){
Field = one.Field??two.Field
//Repeat for all fields
};
}
Then use (handwritten code, don't mind syntax errors)
var mergedResults = (from primary in primaryAccounts
join secondary in secondaryAccounts
on primary.Id equals secondary.Id
select new {Primary=primary, Secondary secondary})
.Select(x=>Merge(x.Primary,x.Secondary);
Second, do merge in the LINQ
Not differs much
var mergedResults = (from primary in primaryAccounts
join secondary in secondaryAccounts
on primary.Id equals secondary.Id
select new Account { Field = primary.Field??secondary.Field}; //Repeat for all fields
Mabe with my extension
public interface IMerge<out T>
{
IEnumerable<IMergeMatched<T>> Matched();
IEnumerable<IMergeMatched<T>> Matched(Func<T, T, bool> predicate);
IEnumerable<T> NotMatchedBySource();
IEnumerable<T> NotMatchedBySource(Func<T, bool> predicate);
IEnumerable<T> NotMatchedByTarget();
IEnumerable<T> NotMatchedByTarget(Func<T, bool> predicate);
}
public interface IMergeMatched<out T>
{
T Source { get; }
T Target { get; }
}
public static class Enumerable
{
public static IMerge<TSource> Merge<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> target,
Func<TSource, TSource, bool> predicate)
{
return new Merge<TSource>(source, target, predicate);
}
}
public class Merge<T> : IMerge<T>
{
private readonly Func<T, T, bool> _predicate;
private readonly IEnumerable<T> _source;
private readonly IEnumerable<T> _target;
private IEnumerable<IMergeMatched<T>> _matcheds;
private IEnumerable<T> _notMatchedBySource;
private IEnumerable<T> _notMatchedByTarget;
public Merge(IEnumerable<T> source, IEnumerable<T> taget, Func<T, T, bool> predicate)
{
_source = source;
_target = taget;
_predicate = predicate;
}
public IEnumerable<IMergeMatched<T>> Matched()
{
if (_matcheds == null)
{
Analize();
}
return _matcheds;
}
public IEnumerable<IMergeMatched<T>> Matched(Func<T, T, bool> predicate)
{
return Matched()
.Where(t => predicate.Invoke(t.Source, t.Target))
.ToArray();
}
public IEnumerable<T> NotMatchedBySource()
{
if (_notMatchedBySource == null)
{
Analize();
}
return _notMatchedBySource;
}
public IEnumerable<T> NotMatchedBySource(Func<T, bool> predicate)
{
return NotMatchedBySource()
.Where(predicate)
.ToArray();
}
public IEnumerable<T> NotMatchedByTarget()
{
if (_notMatchedByTarget == null)
{
Analize();
}
return _notMatchedByTarget;
}
public IEnumerable<T> NotMatchedByTarget(Func<T, bool> predicate)
{
return NotMatchedByTarget()
.Where(predicate)
.ToArray();
}
private void Analize()
{
var macheds = new List<MergeMached<T>>();
var notMachedBySource = new List<T>(_source);
var notMachedByTarget = new List<T>(_target);
foreach (var source in _source)
{
foreach (var target in _target)
{
var macth = _predicate.Invoke(source, target);
if (!macth) continue;
macheds.Add(new MergeMached<T>(source, target));
notMachedBySource.Remove(source);
notMachedByTarget.Remove(target);
}
}
_matcheds = macheds.ToArray();
_notMatchedBySource = notMachedBySource.ToArray();
_notMatchedByTarget = notMachedByTarget.ToArray();
}
}
public class MergeMached<T> : IMergeMatched<T>
{
public MergeMached(T source, T target)
{
Source = source;
Target = target;
}
public T Source { get; private set; }
public T Target { get; private set; }
}
How to use?
[TestMethod]
public void TestMerge()
{
var source = new List<MediaFolder>
{
new MediaFolder
{
Id = "Id1",
Name = "Name1",
Path = "Path1"
},
new MediaFolder
{
Id = "Id2",
Name = "Name2",
Path = "Path2"
},
new MediaFolder
{
Id = "Id3",
Name = "Name3",
Path = "Path3"
},
new MediaFolder
{
Id = "Id4",
Name = "Name4",
Path = "Path4"
},
new MediaFolder
{
Id = "Id5",
Name = "Name5",
Path = "Path5"
},
new MediaFolder
{
Id = "Id6",
Name = "Name6",
Path = "Path6"
}
};
var target = new List<MediaFolder>
{
new MediaFolder
{
Id = "Id1",
Name = "Actualizado en el objeto",
Path = "Path1"
},
//Id2 eliminado
new MediaFolder
{
Id = "Id3",
Name = "Name3",
Path = "Actualizado tambien"
},
new MediaFolder
{
Id = "Id4",
Name = "Name4",
Path = "Path4"
},
new MediaFolder
{
Id = "Id5",
Name = "Name5",
Path = "Path5"
},
new MediaFolder
{
Id = "Id6",
Name = "Name6",
Path = "Path6"
},
new MediaFolder
{
Id = "Id7",
Name = "Nuevo Item 7",
Path = "Nuevo Item 7"
}
};
var merge = source.Merge(target, (x, y) => x.Id == y.Id);
var toUpdate = merge.Matched((x, y) => x.Name != y.Name | x.Path != y.Path)
.ToArray();
var toDelete = merge.NotMatchedBySource();
var toInsert = merge.NotMatchedByTarget();
Assert.AreEqual(2, toUpdate.Count());
Assert.IsTrue(toUpdate.Count(x => x.Source.Id == "Id1" & x.Target.Id == "Id1") > 0);
Assert.IsTrue(toUpdate.Count(x => x.Source.Id == "Id3" & x.Target.Id == "Id3") > 0);
Assert.AreEqual("Id7", toInsert.First().Id);
Assert.AreEqual("Id2", toDelete.First().Id);
}
[TestMethod]
public void TestMerge2()
{
var source = new List<CustomObject>
{
new CustomObject
{
Year = 2010,
Month = 6,
Value = 2
},
new CustomObject
{
Year = 2010,
Month = 7,
Value = 5
},
new CustomObject
{
Year = 2010,
Month = 10,
Value = 3
}
};
var target = new List<CustomObject>
{
new CustomObject
{
Year = 2010,
Month = 7,
Value = 2
},
new CustomObject
{
Year = 2010,
Month = 8,
Value = 1
},
new CustomObject
{
Year = 2010,
Month = 10,
Value = 2
}
};
var merge = source.Merge(target, (x, y) => x.Year == y.Year && x.Month == y.Month);
var toUpdate = merge.Matched((x, y) => x.Value != y.Value)
.ToArray();
var inSourceButNotInTarget = merge.NotMatchedBySource();
var inTargetButNotInSource = merge.NotMatchedByTarget();
Console.WriteLine("Objects to Update");
foreach (var mergeMatched in toUpdate)
{
Console.WriteLine("{0} -{1} - {2} - {3}",
mergeMatched.Source.Year,
mergeMatched.Source.Month,
mergeMatched.Source.Value,
mergeMatched.Target.Value);
}
Console.WriteLine("In source but not in target");
foreach (var customObject in inSourceButNotInTarget)
{
Console.WriteLine("{0} -{1} - {2} - 0",
customObject.Year,
customObject.Month,
customObject.Value);
}
Console.WriteLine("In target but not in source");
foreach (var customObject in inTargetButNotInSource)
{
Console.WriteLine("{0} -{1} - 0 - {2}",
customObject.Year,
customObject.Month,
customObject.Value);
}
}
If you have a simple Linq query like:
var result = from record in db.Customer
select new { Text = record.Name,
Value = record.ID.ToString() };
which is returning an object that can be mapped to a Drop Down List, is it possible to dynamically specify which fields map to Text and Value?
Of course, you could do a big case (switch) statement, then code each Linq query separately but this isn't very elegant. What would be nice would be something like:
(pseudo code)
var myTextField = db.Customer["Name"]; // Could be an enumeration??
var myValueField = db.Customer["ID"]; // Idea: choose the field outside the query
var result = from record in db.Customer
select new { Text = myTextField,
Value = myValueField };
Right way to do this is with closures.
Func<Customer, string> myTextField = (Customer c) => c["Name"];
Func<Customer, int> myValueField = (Customer c) => c["ID"];
var result = from record in db.Customer
select new { Text = myTextField(record),
Value = myValueField(record) };
The one limitation is that your definition of myTextField always needs to return a string.
You could try something like
class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
var dict = new Dictionary<string, Func<Customer, string>>
{ { "ID", (Customer c) => c.ID.ToString() },
{ "Name", (Customer c) => c.Name},
{ "Surname", (Customer c) => c.Surname } };
List<Customer> rows = new List<Customer>();
rows.Add(new Customer { ID = 1, Name = "Foo", Surname = "Bar"});
var list = from r in rows
select new { Text = dict["ID"](r), Value = dict["Name"](r) };
To try to access the properties dynamically, you could try something like
var dict = new Dictionary<string, Func<Customer, string>>
{ { "ID", (Customer c) => c.GetType().GetProperty("ID").GetValue(c,null).ToString() },
{ "Name", (Customer c) => c.GetType().GetProperty("Name").GetValue(c,null).ToString()},
{ "Surname", (Customer c) => c.GetType().GetProperty("Surname").GetValue(c,null).ToString() } };