Grouping and string concatenate in LINQ C# - c#

I'm new to C# linq.
I have data result as follows,
Id | Name | Code
1 | Go | GOS
1 | RES | RECSO
1 | ICS | ICSO
2 | Go | GOS
2 | ICS | ICSO
And I want the result as follows,
Id | Name | Code
1, Go,RES,ICS | GOS,RECSO,ICSO
2, Go,ICS | GOS,ICSO
Can some one provide the linq query for this, with optimized way, as I have big data set.
Thanks in advance

var result = data.GroupBy(g => g.Id)
.Select(s => new {
Id = s.Key,
Name = string.Join(",", s.Select(ss => ss.Name)),
Code = string.Join(",", s.Select(ss => ss.Code)),
});

you need to group your data by Id and then use string.Join to get comma separated values for Name and Code
var query = from m in Data
group m by m.Id into grp
select new {ID = grp.Key
,Name= string.Join(",",grp.Select(g=>g.Name ))
,Code= string.Join(",",grp.Select(g=>g.Code )) };
If your data source is EF or LinqtoSQL or any other ORM then use Data.AsEnumerable() to force grouping and string concatenation in memory
otherwise It will give Error (in case of EF) or it will generate SQL query for each row (LinqToSQL)

How about this:
The LINQ part:
List<BeforeData> listBefore = new List<BeforeData>();
Dictionary<int, List<AfterData>> listAfter = listBefore
.GroupBy(it => it.ID)
.ToDictionary(x => x.Key, y => y.Select(z => new AfterData(z.name, z.code)).ToList());
Assuming the classes:
public class BeforeData
{
public int ID;
public string name, code;
public BeforeData(int ID, string name, string code)
{
this.ID = ID;
this.name = name;
this.code = code;
}
}
public class AfterData
{
public string name, code;
public AfterData(string name, string code)
{
this.name = name;
this.code = code;
}
}
Edit: Simplified LINQ

Related

Linq to sql query check for equality of tuples

How write linq to sql query to retrieve records from table below that (B ,C) be in list c.
Table is in database.
var c = new List<(int,int)>{(1,4), (3,6)};
+---+---+---+
| A | B | C |
+---+---+---+
| a | 1 | 4 |
| b | 2 | 5 |
| c | 3 | 6 |
+---+---+---+
Query should return a and c.
If you are talking about tuples being "fully"(all elements are sequentially equal) equal then you can just use contains:
var c = new List<(int,int)>{(1,3), (3,6)};
var table = new List<(int,int)>{(1,3), (2,5), (3,6)};
var res = table
.Where(i => c.Contains(i))
.ToList();
Equality and tuples
If #Caius Jard's assumption is right then just change .Where(i => c.Contains(i)) to .Where(i => !c.Contains(i))
I am not sure how your logic is but i think you are trying to do something like this,
Let's assume your table model looks like below,
public class MyTable {
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public MyTable(string a, string b, string c) {
A = a;
B = b;
C = c;
}
}
And let's fill the data you have shared and query,
var c = new List<(int, int)> { (1, 4), (3, 6) };
List<MyTable> myTables = new List<MyTable>();
myTables.Add(new MyTable("a", "1", "4"));
myTables.Add(new MyTable("b", "2", "5"));
myTables.Add(new MyTable("c", "3", "6"));
var res = myTables.Where(x => c.Any(y => y.Item1.ToString() == x.B && y.Item2.ToString() == x.C)).Select(x => x.A);
Console.WriteLine(string.Join(" ", res));
Console.ReadKey();
This will print:
a c

select distinct name and combine an objects property in a List<T>

I have have a List below, and I am trying to combine the T.name property and sum up the T.score property
Data
Name | score
-------------
Jon | 50
Jon | 100
Ash | 100
Ash | 75
Desired result is
Jon | 150
Ash | 175
I am able to do this in LINQ and create a var with the desired results
List<PieSeriesData> PiSeriesData = new List<PieSeriesData>();
var x = PiSeriesData.GroupBy(i => i.Name)
.Select(g => new { Id = g.Key, Total = g.Sum(i => i.Score) })
.ToList();
The issue with that is I need x to be a List type, which is what the HighCharts nuget requires.
As I understand from the chat you need the output to be a List<PieSeriesData>. You need to project a new object of PieSeriesData class in the select and not of an anonymous type: _See that you also need to assign the properties that exist and not use new ones like Id and Total:
List<PieSeriesData> PiSeriesData = new List<PieSeriesData>();
var x = PiSeriesData.GroupBy(i => i.Name)
.Select(g => new PieSeriesData {
Name = g.Key,
Score = g.Sum(i => i.Score)
}).ToList();
// Now x is of type: List<PieSeriesData>
Why not create new class
public class PieSeriesRenderData
{
public string ID { get; set; }
public int Total { get; set; }
}
And after that return this:
List<PieSeriesData> piSeriesData = new List<PieSeriesData>();
var x = piSeriesData.GroupBy(i => i.Name)
.Select(g => new PieSeriesRenderData { Id = g.Key, Total = g.Sum(i => i.Score) })
.ToList();
Now you will have object of List<PieSeriesRenderData> which you can return it as Json from your web service and you will render the highchart in your ajax.successful method.

Rewriting this SQL in Lambda - using count and group by

I'm stuck rewriting this SQL in Lambda:
SELECT TOP 1000 COUNT(LoginDateTime)
,[LoginDateTime]
,[Culture]
FROM [LearningApp].[dbo].[UserActivities]
group by LoginDateTime, Culture
Result:
+-----+---------------------------+----------+
| | LoginDateTime | Culture |
+-----+---------------------------+----------+
| 1 | 2016-07-14 12:21:23.307 | de |
| 4 | 2016-07-13 12:21:23.307 | en |
| 2 | 2016-07-14 12:21:23.307 | en |
+-----+---------------------------+----------+
And my code:
public List<UserActivityResponseContract> GetUserActivity()
{
var userActivityResponseContracts = new List<UserActivityResponseContract>();
var userActivitiesList = _baseCommands.GetAll<UserActivity>()
.Select(x => new
{
x.LoginDateTime,
x.Culture
})
.GroupBy(x => new { x.LoginDateTime, x.Culture});
foreach (var userActivity in userActivitiesList)
{
userActivityResponseContracts.Add(new UserActivityResponseContract
{
ActivityDate = userActivity.Key.LoginDateTime.ToShortDateString(),
NumberOfTimesLoggedIn = userActivity.Count(),
Culture = userActivity.Key.Culture
});
}
return userActivityResponseContracts;
}
It doesn't seem very difficult but I am a bit stuck.
Method Syntax:
var result = _baseCommands.GetAll<UserActivity>()
.GroupBy(x => new { x.LoginDateTime, x.Culture})
.Select (x => new UserActivityResponseContract
{
ActivityDate = x.Key.LoginDateTime.ToShortDateString(),
Culture = x.Key.Culture,
NumberOfTimesLoggedIn = x.Count()
})
.Take(1000).ToList();
You can also use an overload of GroupBy that enables you to pass the select function as a second parameter
Query Syntax:
var result = (from x in _baseCommands.GetAll<UserActivity>()
group x by new { x.LoginDateTime, x.Culture} into g
select new UserActivityResponseContract
{
ActivityDate = g.Key.LoginDateTime.ToShortDateString(),
Culture = g.Key.Culture,
NumberOfTimesLoggedIn = g.Count()
}).Take(1000).ToList();
To GroupBy just the Date part of this DateTime do: x.LoginDateTime.Date

I want full row detail with use of distinct in my linq query

I have a table with the details of property
------------------------------------------------
Id | Propertyname |date_created |Price |agent_name
------------------------------------------------
1 |xxxxxx |17-06-2015 |10000 |abc
2 |xxyyyyy |15-06-2015 |10000 |abc
3 |xxyyyyy |16-06-2015 |10000 |bcd
4 |xxyyyyy |15-06-2015 |10000 |bcd
5 |xxyyyyy |17-06-2015 |10000 |cde
6 |xxyyyyy |15-06-2015 |10000 |cde
I want a result at
------------------------------------------------
Id | Propertyname |date_created |Price |agent_name
------------------------------------------------
1 |xxxxxx |17-06-2015 |10000 |abc
3 |xxyyyyy |16-06-2015 |10000 |bcd
5 |xxyyyyy |17-06-2015 |10000 |cde
I want a result with distinct of agent_name with full row details
I tried below code
var property =
from l in properties
group l by l.agent_name into g
let lastUsed = g.OrderBy(x => x.date_created).Last()
select lastUsed;
Adove code displayed error
LINQ to Entities does not recognize the method 'properties Lastproperties' method, and this method cannot be translated into a store expression.
Anyone know how to solve this
var property=(from n in bc.db.properties select n).GroupBy(x => x.agent_name)
.Where(g => g.Count() == 1)
.Select(g => g.FirstOrDefault()).OrderByDescending(x=>x.date_created)
Created following code on LinqPad and it works, let me figure out if there's a better and much more optimized version of the solution
void Main()
{
var result = Detail.Create();
var final = result.GroupBy(s=>s.agent).ToDictionary(p=>p.Key,p=>p.OrderBy(n=>n.dateTime).Last())
.Select(a=>new
{
a.Value.id,
a.Value.prop,
a.Value.dateTime,
a.Value.price,
agent = a.Key
});
foreach(var x in final)
{
Console.WriteLine(x.id + " :: " + x.prop + " :: " + x.dateTime+ " :: " + x.price+ " :: " +x.agent);
}
}
public class Detail
{
public Detail(int i, string p, DateTime d, int pr, string a)
{
id= i;
prop = p;
dateTime = d;
price = pr;
agent = a;
}
public int id;
public string prop;
public DateTime dateTime;
public int price;
public string agent;
public static List<Detail> Create()
{
List<Detail> list = new List<Detail>();
list.Add(new Detail(1, "xxxxxx", DateTime.Parse("17-06-2015"), 10000,"abc"));
list.Add(new Detail(2, "xxyyyyy", DateTime.Parse("15-06-2015"), 10000,"abc"));
list.Add(new Detail(3, "xxyyyyy", DateTime.Parse("16-06-2015"), 10000,"bcd"));
list.Add(new Detail(4, "xxyyyyy", DateTime.Parse("15-06-2015"), 10000,"bcd"));
list.Add(new Detail(5, "xxyyyyy", DateTime.Parse("17-06-2015"), 10000,"cde"));
list.Add(new Detail(6, "xxyyyyy", DateTime.Parse("15-06-2015"), 10000,"cde"));
return list;
}
}

GROUP BY in datatable on two columns with sum of third column using LINQ

I am searching for a LINQ query for which input and output datatables are as below -
Name Code count
-------------------------
User1 q1 2
user1 q2 2
user2 q2 1
user2 q3 3
user1 q1 2
Name Code Count
-----------------------
User1 | q1 | 4
User1 | q2 | 2
User1 | q3 | 0
User2 | q1 | 0
User2 | q2 | 1
User2 | q3 | 3
i.e. I want sum of count for distinct set of codes for each Name, in result table, it showing (user1, q3,0) because there is no record of q3 for user1 in input datatable, Please help in this regard, Thanks again
This should work:
var query = table.AsEnumerable()
.GroupBy(row => new { Name = row.Field<string>("Name"), Code = row.Field<string>("Code") });
var table2 = table.Clone(); // empty table with same schema
foreach (var x in query)
{
string name = x.Key.Name;
string code = x.Key.Code;
int count = x.Sum(r => r.Field<int>("Count"));
table2.Rows.Add(x.Key.Name, x.Key.Code, count);
}
Edit: if you instead want to modify the original table and sum the count per each name-code group, use this approach which uses a Dictionary as lookup:
var nameCodeCountLookup = table.AsEnumerable()
.GroupBy(row => new { Name = row.Field<string>("Name"), Code = row.Field<string>("Code") })
.ToDictionary(ncGrp => ncGrp.Key, ncGrp => ncGrp.Sum(r => r.Field<int>("Count")));
foreach (DataRow row in table.Rows)
{
string Name = row.Field<string>("Name");
string Code = row.Field<string>("Code");
row.SetField("Count", nameCodeCountLookup[new { Name, Code }]);
}
However, i don't know why your result table contains this row:
User1 | q3 | 0
There is no name-code combination User1+q3 in the original table.
I think here is what you looking for:
var result =
new DataTable().Rows.Cast<DataRow>()
.Select(p => new {User = p[0], Q = p[1], Value = (double) p[2]})
.GroupBy(p => new {p.User, p.Q})
.Select(p => new {User = p.Key.User, Q = p.Key.Q, Total = p.Sum(x => x.Value)})
.ToList();
Or almost same, but you can do it too:
var result =
new DataTable().Rows.Cast<DataRow>()
.Select(p => new
{
User = p.Field<string>("column1Name"),
Q = p.Field<string>("column2Name"),
Value = p.Field<double>("column3Name")
})
.GroupBy(p => new {p.User, p.Q})
.Select(p => new {User = p.Key.User, Q = p.Key.Q, Total = p.Sum(x => x.Value)})
.ToList();

Categories

Resources