Use LINQ to concatenate multiple rows into single row (CSV property) - linq

I'm looking for the LINQ equivalent to the Sybase's LIST() or MySQL's group_concat()
It'll convert:
User Hobby
--------------
Bob Football
Bob Golf
Bob Tennis
Sue Sleeping
Sue Drinking
To:
User Hobby
--------------
Bob Football, Golf, Tennis
Sue Sleeping, Drinking

That's the GroupBy operator. Are you using LINQ to Objects?
Here's an example:
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
static void Main()
{
var users = new[]
{
new { User="Bob", Hobby="Football" },
new { User="Bob", Hobby="Golf" },
new { User="Bob", Hobby="Tennis" },
new { User="Sue", Hobby="Sleeping" },
new { User="Sue", Hobby="Drinking" },
};
var groupedUsers = users.GroupBy(user => user.User);
foreach (var group in groupedUsers)
{
Console.WriteLine("{0}: ", group.Key);
foreach (var entry in group)
{
Console.WriteLine(" {0}", entry.Hobby);
}
}
}
}
That does the grouping - can you manage the rest yourself?

See if this solution helps you:
List<User> users = new List<User>()
{
new User {Name = "Bob", Hobby = "Football" },
new User {Name = "Bob", Hobby = "Golf"},
new User {Name = "Bob", Hobby = "Tennis"},
new User {Name = "Sue", Hobby = "Sleeping"},
new User {Name = "Sue", Hobby = "Drinking"}
};
var groupedUsers = from u in users
group u by u.Name into g
select new
{
Name = g.First<User>().Name,
Hobby = g.Select(u => u.Hobby)
};
foreach (var user in groupedUsers)
{
Console.WriteLine("Name: {0}", user.Name);
foreach (var hobby in user.Hobby)
{
Console.WriteLine("Hobby: {0}", hobby);
}
}

re the _concat aspect of your question, using:
static class EnumerableExtensions
{
public static String AsJoined( this IEnumerable<String> enumerable )
{
return AsJoined( enumerable, "," );
}
public static String AsJoined( this IEnumerable<String> enumerable, String separator )
{
return String.Join( separator, enumerable.ToArray() );
}
}
The outputting foreach in bruno conde and Jon Skeet's answers can become:
Console.WriteLine( "User:\tHobbies");
foreach ( var group in groupedUsers )
Console.WriteLine( "{0}:\t{1}", group.Key, group.Select( g => g.Hobby ).AsJoined( ", " ) );
... and you'll get the precise result output format you asked for (yes, I know the others have already solved your problem, but its hard to resist!)

Or else we can do the following-
var users = new[]
{
new { User="Bob", Hobby="Football" },
new { User="Bob", Hobby="Golf" },
new { User="Bob", Hobby="Tennis" },
new { User="Sue", Hobby="Sleeping" },
new { User="Sue", Hobby="Drinking" },
};
var userList = users.ToList();
var ug = (from user in users
group user by user.User into groupedUserList
select new { user = groupedUserList.Key, hobby = groupedUserList.Select(g =>g.Hobby)});
var ug2 = (from groupeduser in ug
select new{ groupeduser.user, hobby =string.Join(",", groupeduser.hobby)});

To do it in one Linq Statement. There is no way I'd recommend the code, but it shows that it could be done.
var groupedUsers = from user in users
group user by user.User into userGroup
select new
{
User = userGroup.Key,
userHobies =
userGroup.Aggregate((a, b) =>
new { User = a.User, Hobby = (a.Hobby + ", " + b.Hobby) }).Hobby
}
;
foreach (var x in groupedUsers)
{
Debug.WriteLine(String.Format("{0} {1}", x.User, x.userHobies));
}

all answers is not good enough;
because this is a db query,but all of us do that just in memory;
diff is that some operation in memory will occuce a error can't trans to store expression;
var list = db.Users.GroupBy(s=>s.User).
select(g=>new{user=g.Key,hobbys=g.select(s=>s.Hobby)}); // you can just do that from db
var result=list.ToList(); // this is important,to query data to memory;
var result2 = result.select(g=>new{user=g.Key,hobbyes=string.join(",",g.hobbyes)}; //then,do what you love in memory

Related

Concatenating the values of one column into a single row

I have the result of a database query for a single user as:
Name | Role
Tom | Admin
Tom | Manager
I want to convert the result into an object:
{ Name = "Tom", Roles = "Admin, Manager" }
I can do it using a foreach and too many statements. How can it be done using a LINQ query?
The sample code can be:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var result = new [] { new { Name = "Tom", Role = "Admin"},
new { Name = "Tom", Role="Manager"}
};
string roles = "";
foreach (var u in result)
roles += "," + u.Role;
Console.WriteLine(result[0].Name + " " + roles);
}
}
You need to use Linq.GroupBy Name and select concatenating Roles by using String.Join:
var groupedResult = result.GroupBy(g => g.Name)
.Select(s => new
{
Name = s.Key,
Roles = String.Join(",", s.Select(i => i.Role))
});
It will give you a list, if you want to get first item of list you can use:
var firstItem = groupedResult.FirstOrDefault();

How to iterate through GroupBy groups using Dynamic LINQ? [duplicate]

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);
}
}

PIVOT with LINQ from Datatable [duplicate]

I have a collection of items that contain an Enum (TypeCode) and a User object, and I need to flatten it out to show in a grid. It's hard to explain, so let me show a quick example.
Collection has items like so:
TypeCode | User
---------------
1 | Don Smith
1 | Mike Jones
1 | James Ray
2 | Tom Rizzo
2 | Alex Homes
3 | Andy Bates
I need the output to be:
1 | 2 | 3
Don Smith | Tom Rizzo | Andy Bates
Mike Jones | Alex Homes |
James Ray | |
I've tried doing this using foreach, but I can't do it that way because I'd be inserting new items to the collection in the foreach, causing an error.
Can this be done in Linq in a cleaner fashion?
I'm not saying it is a great way to pivot - but it is a pivot...
// sample data
var data = new[] {
new { Foo = 1, Bar = "Don Smith"},
new { Foo = 1, Bar = "Mike Jones"},
new { Foo = 1, Bar = "James Ray"},
new { Foo = 2, Bar = "Tom Rizzo"},
new { Foo = 2, Bar = "Alex Homes"},
new { Foo = 3, Bar = "Andy Bates"},
};
// group into columns, and select the rows per column
var grps = from d in data
group d by d.Foo
into grp
select new {
Foo = grp.Key,
Bars = grp.Select(d2 => d2.Bar).ToArray()
};
// find the total number of (data) rows
int rows = grps.Max(grp => grp.Bars.Length);
// output columns
foreach (var grp in grps) {
Console.Write(grp.Foo + "\t");
}
Console.WriteLine();
// output data
for (int i = 0; i < rows; i++) {
foreach (var grp in grps) {
Console.Write((i < grp.Bars.Length ? grp.Bars[i] : null) + "\t");
}
Console.WriteLine();
}
Marc's answer gives sparse matrix that can't be pumped into Grid directly.
I tried to expand the code from the link provided by Vasu as below:
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
return source.GroupBy(key1Selector).Select(
x => new
{
X = x.Key,
Y = source.GroupBy(key2Selector).Select(
z => new
{
Z = z.Key,
V = aggregate(from item in source
where key1Selector(item).Equals(x.Key)
&& key2Selector(item).Equals(z.Key)
select item
)
}
).ToDictionary(e => e.Z, o => o.V)
}
).ToDictionary(e => e.X, o => o.Y);
}
internal class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public string Function { get; set; }
public decimal Salary { get; set; }
}
public void TestLinqExtenions()
{
var l = new List<Employee>() {
new Employee() { Name = "Fons", Department = "R&D", Function = "Trainer", Salary = 2000 },
new Employee() { Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3000 },
new Employee() { Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 4000 },
new Employee() { Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5000 },
new Employee() { Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6000 },
new Employee() { Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 2000 }};
var result5 = l.Pivot3(emp => emp.Department, emp2 => emp2.Function, lst => lst.Sum(emp => emp.Salary));
var result6 = l.Pivot3(emp => emp.Function, emp2 => emp2.Department, lst => lst.Count());
}
* can't say anything about the performance though.
You can use Linq's .ToLookup to group in the manner you are looking for.
var lookup = data.ToLookup(d => d.TypeCode, d => d.User);
Then it's a matter of putting it into a form that your consumer can make sense of. For instance:
//Warning: untested code
var enumerators = lookup.Select(g => g.GetEnumerator()).ToList();
int columns = enumerators.Count;
while(columns > 0)
{
for(int i = 0; i < enumerators.Count; ++i)
{
var enumerator = enumerators[i];
if(enumator == null) continue;
if(!enumerator.MoveNext())
{
--columns;
enumerators[i] = null;
}
}
yield return enumerators.Select(e => (e != null) ? e.Current : null);
}
Put that in an IEnumerable<> method and it will (probably) return a collection (rows) of collections (column) of User where a null is put in a column that has no data.
I guess this is similar to Marc's answer, but I'll post it since I spent some time working on it. The results are separated by " | " as in your example. It also uses the IGrouping<int, string> type returned from the LINQ query when using a group by instead of constructing a new anonymous type. This is tested, working code.
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 1, UserName = "Mike Jones"},
new { TypeCode = 1, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
var Columns = from i in Items
group i.UserName by i.TypeCode;
Dictionary<int, List<string>> Rows = new Dictionary<int, List<string>>();
int RowCount = Columns.Max(g => g.Count());
for (int i = 0; i <= RowCount; i++) // Row 0 is the header row.
{
Rows.Add(i, new List<string>());
}
int RowIndex;
foreach (IGrouping<int, string> c in Columns)
{
Rows[0].Add(c.Key.ToString());
RowIndex = 1;
foreach (string user in c)
{
Rows[RowIndex].Add(user);
RowIndex++;
}
for (int r = RowIndex; r <= Columns.Count(); r++)
{
Rows[r].Add(string.Empty);
}
}
foreach (List<string> row in Rows.Values)
{
Console.WriteLine(row.Aggregate((current, next) => current + " | " + next));
}
Console.ReadLine();
I also tested it with this input:
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 3, UserName = "Mike Jones"},
new { TypeCode = 3, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
Which produced the following results showing that the first column doesn't need to contain the longest list. You could use OrderBy to get the columns ordered by TypeCode if needed.
1 | 3 | 2
Don Smith | Mike Jones | Tom Rizzo
| James Ray | Alex Homes
| Andy Bates |
#Sanjaya.Tio I was intrigued by your answer and created this adaptation which minimizes keySelector execution. (untested)
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
var lookup = source.ToLookup(x => new {Key1 = key1Selector(x), Key2 = key2Selector(x)});
List<TKey1> key1s = lookup.Select(g => g.Key.Key1).Distinct().ToList();
List<TKey2> key2s = lookup.Select(g => g.Key.Key2).Distinct().ToList();
var resultQuery =
from key1 in key1s
from key2 in key2s
let lookupKey = new {Key1 = key1, Key2 = key2}
let g = lookup[lookupKey]
let resultValue = g.Any() ? aggregate(g) : default(TValue)
select new {Key1 = key1, Key2 = key2, ResultValue = resultValue};
Dictionary<TKey1, Dictionary<TKey2, TValue>> result = new Dictionary<TKey1, Dictionary<TKey2, TValue>>();
foreach(var resultItem in resultQuery)
{
TKey1 key1 = resultItem.Key1;
TKey2 key2 = resultItem.Key2;
TValue resultValue = resultItem.ResultValue;
if (!result.ContainsKey(key1))
{
result[key1] = new Dictionary<TKey2, TValue>();
}
var subDictionary = result[key1];
subDictionary[key2] = resultValue;
}
return result;
}

Entity Framework 4.0 with Linq

public IEnumerable<Models.Comment> GetUserComments()
{
return List<Comment>
{
new Comment
{
CommentFor = "ee",
DateAdded = DateTime.Now,
CommentText = "aaaa",
Location = new Location
{
Name = "Location Name",
Country = new Country
{
Name="Israel"
},
State=new State { Name="TelAviv" }
}
}
};
}
Can you help me correct Linq query for that?
I need to take value from database using Entity Framework 4.
I did like this
public IEnumerable<Models.Comment> GetUserComments()
{
var comment = (from u in context.Comments
where u.UserID == userId
select new Comment
{
//Location = context.Locations.FirstOrDefault(x => x.locationid == u.LocationID).name,
Location = (from l in context.Locations
where l.LocationID == u.LocationID
select new Location
{
Name = l.Name,
State = (
from s in context.States
where (s.StateID == l.StateID)
select new State { Name = s.Name }
).FirstOrDefault()
}
).FirstOrDefault(),
CommentFor = "bs",
DateAdded = u.DateAdded,
CommentText = u.CommentText
}
).ToList();
}
getting error like:
The entity or complex type 'CGWeb.Models.Repositories.Comment' cannot be constructed in a LINQ to Entities query.
Please tell me where my mistake i had done
u.Location should be Location.
select new Comment
{
u.Location //<- remove the u.
Try this
var comment = (from u in context.Comments
where u.UserID == userId
select new Comment
{
Location = context.Locations.FirstOrDefault(x=>x.LocationID==u.LocationID).Name,
CommentFor = "Bb",
DateAdded = u.DateAdded,
CommentText = u.CommentText
}
).ToList();

Linq Convert to Custom Dictionary?

.NET 4, I have
public class Humi
{
public int huKey { get; set; }
public string huVal { get; set; }
}
And in another class is this code in a method:
IEnumerable<Humi> someHumi = new List<Humi>(); //This is actually ISingleResult that comes from a LinqToSql-fronted sproc but I don't think is relevant for my question
var humia = new Humi { huKey = 1 , huVal = "a"};
var humib = new Humi { huKey = 1 , huVal = "b" };
var humic = new Humi { huKey = 2 , huVal = "c" };
var humid = new Humi { huKey = 2 , huVal = "d" };
I want to create a single IDictionary <int,string[]>
with key 1 containing ["a","b"] and key 2 containing ["c","d"]
Can anyone point out a decent way to to that conversion with Linq?
Thanks.
var myDict = someHumi
.GroupBy(h => h.huKey)
.ToDictionary(
g => g.Key,
g => g.ToArray())
Create an IEnumerable<IGrouping<int, Humi>> and then project that into a dictionary. Note .ToDictionary returns a Dictionary, not an IDictionary.
You can use ToLookup() which allows each key to hold multiple values, exactly your scenario (note that each key would hold an IEnumerable<string> of values though not an array):
var myLookup = someHumi.ToLookup(x => x.huKey, x => x.huVal);
foreach (var item in myLookup)
{
Console.WriteLine("{0} contains: {1}", item.Key, string.Join(",", item));
}
Output:
1 contains: a,b
2 contains: c,d

Resources