In my WebAPI, I'm using EF. I'm currently getting my data out using a LINQ statement.
My LINQ is:
var output = _context.CarMakes
.Select(x => new
{
id= x.CarId,
make = x.CarMake,
year = x.Year,
status = x.Status
});
my data is returned as:
[
{
"id": 1,
"make": "Ford",
"year" : 2020,
"model" : "Focus"
},
{
"id" : 2,
"make" :"Ford",
"year" : 1994,
"model" : "F-150"
},
{
"id" : 3,
"make" : "Chevy",
"year" : 2022,
"model" : "Silverado"
}
]
How can I get it returned so it's grouped so I can use it in a TreeView navigation menu such as: What changes should I make to the .NET code?
Ford:
2020
Focus
1994
F-150
Chevy
2022
Silverado
you need to use group by to get the result needed, here is an example :
var result = _context.CarMakes.GroupBy(x => x.make)
.Select(g => new {
Make = g.Key,
Models = g.GroupBy(x => x.year)
.Select(y => new {
Year = y.Key,
Name = y.Select(z => z.model)
})
});
You have to perform group by on make and year fields and then select all the cars that come under those grouping. Then in the front end, you can use this returned data to make a nested tree view.
var output = _context.CarMakes
.Select(x => new
{
id = x.CarId,
make = x.CarMake,
year = x.Year,
status = x.Status
})
.GroupBy(x => new { x.make, x.year })
.Select(g => new
{
make = g.Key.make,
year = g.Key.year,
cars = g.Select(x => new { id = x.id, status = x.status })
});
Related
I'm new to C# programming and any help would appreciate. because this might be a simple thing to a c# developer. I have tried so many things but had type mismatches or compilation errors in Newtonsoft JObjects when I try to develop this.
I'm having a list of objects like
List<ContextResult> formOutputs ->
[0] = (section 1, Button, true),
[1] = (section 1, TextBox, anyname),
[2] = (section 2, Button, false)
public class ContextResult
{
public string Section { get; set; }
public string Key{ get; set; }
public string Value { get; set; }
}
I need to sort this into sections, and convert into a list of JObjects using Newtonsoft.Json.Linq. The output should look like this in JSON format,
"section 1":{
"Button": "true",
"TextBox": "anyname"
},
"section 2":{
"Button": "false"
}
please note that I have already ordered the formOutputs to ascending order and grouped using GroupBy to remove duplicates.
Instead of working with JObjects I'd convert you data into desired structure and then just serialize it.
Here's how it can be done for your data:
var o = formOutputs
.GroupBy(o => o.Section)
.ToDictionary(g => g.Key,g => g.ToDictionary(x => x.Key, x => x.Value));
Here's a version with comments:
var o = formOutputs
.GroupBy( o => o.Section) // For the example data we now have groups: 'section 1', 'section 2'
.ToDictionary(
keySelector: g => g.Key, // this 'Key' is the group's key (so o.Section), not the Key property of ContextResult
elementSelector: g => g.ToDictionary(
keySelector: x => x.Key,
elementSelector: x => x.Value));
Test:
var formOutputs = new List<ContextResult> {
new ContextResult { Section = "section 1", Key = "Button", Value = "true"},
new ContextResult { Section = "section 1", Key = "TextBox",Value = "anyname"},
new ContextResult { Section = "section 2", Key = "Button", Value = "false"}
};
var o = formOutputs
.GroupBy(o => o.Section)
.ToDictionary(g => g.Key,g => g.ToDictionary(x => x.Key, x => x.Value));
Console.WriteLine(JsonConvert.SerializeObject(o, Formatting.Indented));
Output:
{
"section 1": {
"Button": "true",
"TextBox": "anyname"
},
"section 2": {
"Button": "false"
}
}
My data looks like this:
I am trying to create a JSON ouput (using JSON .NET) which will have the sum of the Value column by levels.
For example:
{
'id': 'AB',
'sum': '53',
'level2' : [
{
'id' : 'CD',
'sum' : '23',
'level3' : [
{
'id' : 'd1',
'sum' : '12'
},
{
'id' : 'd2',
'sum' : '11'
}
]
...
I am trying to use LINQ to create this. So far I have the following code:
var query = reader.SelectRows(r => new
{
level1 = r["level1"].ToString(),
sum = r["sum"] != DBNull.Value ? Convert.ToDouble(r["sum"]) : 0,
level2 = new
{
level2 = r["level2"].ToString(),
sum = r["sum "] != DBNull.Value ? Convert.ToDouble(r["sum"]) : 0,
level3 = new
{
level3 = r["level3 "].ToString(),
sum = r["sum"] != DBNull.Value ? Convert.ToDouble(r["sum"]) : 0
}
}
})
.GroupBy(r => new { r.level1 })
.Select(g => new
{
id = g.Key.level1,
sum = g.Sum(x => x.sum),
level2 = g.GroupBy(l => new { l.level2.level2 })
.Select(l => new
{
id = l.Key.level2,
sum = g.Sum(y => y.sum),
level3 = l.GroupBy(m => new { m.level2.level3.level3 })
.Select(m => new
{
id = m.Key.level3,
sum = g.Sum(z => z.sum),
})
})
});
retJSON = JsonConvert.SerializeObject(new { data = query }, Formatting.Indented);
The SelectRows function is like this:
// Adapted from this answer https://stackoverflow.com/a/1202973
// To https://stackoverflow.com/questions/1202935/convert-rows-from-a-data-reader-into-typed-results
// By https://stackoverflow.com/users/3043/joel-coehoorn
public static IEnumerable<T> SelectRows<T>(this IDataReader reader, Func<IDataRecord, T> select)
{
while (reader.Read())
{
yield return select(reader);
}
}
However, I am getting sum repeated at every level, i.e. the same value. Any direction on how to achieve this will be greatly appreciated.
I think your query is nearly there just 2 changes (shown by comments below).
.GroupBy(r => new { r.level1 })
.Select(g => new
{
id = g.Key.level1,
sum = g.Sum(x => x.sum),
level2 = g.GroupBy(l => new { l.level2.level2 })
.Select(l => new
{
id = l.Key.level2,
sum = l.Sum(y => y.sum), //l not g
level3 = l.GroupBy(m => new { m.level2.level3.level3 })
.Select(m => new
{
id = m.Key.level3,
sum = m.Sum(z => z.sum), //m not g
})
})
});
I have the following table.
Name Date
A 10/04/2016
A 10/03/2016
A 10/05/2016
B 10/02/2016
B 10/01/2016
B 10/03/2016
C 10/05/2016
C 10/02/2016
C 10/04/2016
I would like to display this
Name Date
B 10/01/2016
B 10/02/2016
B 10/03/2016
C 10/02/2016
C 10/04/2016
C 10/05/2016
A 10/03/2016
A 10/04/2016
A 10/05/2016
How to create a query to get this result.
var elements = new[]
{
new { Name = "A", Date = DateTime.Parse("10/04/2016") },
new { Name = "A", Date = DateTime.Parse("10/03/2016") },
new { Name = "A", Date = DateTime.Parse("10/05/2016") },
new { Name = "B", Date = DateTime.Parse("10/02/2016") },
new { Name = "B", Date = DateTime.Parse("10/01/2016") },
new { Name = "B", Date = DateTime.Parse("10/03/2016") },
new { Name = "C", Date = DateTime.Parse("10/05/2016") },
new { Name = "C", Date = DateTime.Parse("10/02/2016") },
new { Name = "C", Date = DateTime.Parse("10/04/2016") },
};
// LINQ to Objects
elements
.GroupBy(e => e.Name) // grouping by name
.Select(group => group.OrderBy(e => e.Date)) // order elements by date
.OrderBy(group => group.First().Date) // order groups by date
.SelectMany(group => group); // compose groups
// LINQ to Entities
elements
.GroupBy(e => e.Name)
.Select(group => group.OrderBy(e => e.Date))
.OrderBy(group => group.FirstOrDefault().Date)
.AsEnumerable()
.SelectMany(group => group);
You probably do not need groupby but only orderby can fullfil your task:
yourList.OrderBy(l => l.Date).ThenBy(l => l.Name)
It would be great if you provide more information. Such as, the object type containing the data. Is it a custom model or a DataTable? Any how, I will try both.
var result = dataTable.Rows.AsEnumerable().GroupBy(x => x.Name).OrderBy(x => x.Date);
OR
var result = Foo.GroupBy(x => x.Name).OrderBy(x => x.Date);
https://dotnetfiddle.net/0SfhAz
This is the fiddle with the code. For some reason, I can't get the fiddle to write out the values in the list.
I originally did this in LinqPad, and here is the output I got from the code in the fiddle when in LinqPad
B 10/1/2016
B 10/2/2016
B 10/3/2016
C 10/2/2016
C 10/4/2016
C 10/5/2016
A 10/3/2016
A 10/4/2016
A 10/5/2016
I don't know why I can't just do a writeline on the .net fiddle, but it is being difficult. In case the fiddle doesn't work for you, here it the linq query I used for this
var orderedList = originalList.OrderBy(x => x.SomeDate).GroupBy(y => y.Name);
There may be better approaches, but this looked to solve your issue
I have two List row1 and row2.This is data for row1:
and data for row2:
I Concatenate these two lists into one :
var rows = rows1.Concat(rows2).ToList();
The result would be this:
and then want to groupBy on a few fields and order by with other fields.and do some changes to some data. This is my Code
var results = rows.GroupBy(row => new { row.FromBayPanel, row.TagNo })
.SelectMany(g => g.OrderBy(row => row.RowNo)
.Select((x, i) =>
new
{
TagGroup = x.TagGroup,
RowNo = (i == 0) ? (j++).ToString() : "",
TagNo = (i == 0) ? x.TagNo.ToString() : "",
FromBayPanel = x.FromBayPanel,
totalItem = x.totalItem
}).ToList());
which brings me back this result:
This is not what I really want I want to have this result. I Want all data with same "FromBayPanel" be listed together.
which part of my code is wrong?
I think when you want to order the elements within your group you have to use a different approach as SelectMany will simply flatten your grouped items into one single list. Thus instead of rows.GroupBy(row => new { row.FromBayPanel, row.TagNo }).SelectMany(g => g.OrderBy(row => row.RowNo) you may use this:
rows.OrderBy(x => x.FromBayPanel).ThenBy(x => x.TagNo) // this preserves the actual group-condition
.ThenBy(x => x.RowNo) // here you order the items of every item within the group by its RowNo
.GroupBy(row => new { row.FromBayPanel, row.TagNo })
.Select(...)
EDIT: You have to make your select WITHIN every group, not afterwards:
rows.GroupBy(row => new { row.FromBayPanel, row.TagNo })
.ToDictionary(x => x.Key,
x => x.OrderBy(y => y.RowNo)
.Select((y, i) =>
new
{
TagGroup = y.TagGroup,
RowNo = (i == 0) ? (j++).ToString() : "",
TagNo = (i == 0) ? y.TagNo.ToString() : "",
FromBayPanel = x.FromBayPanel,
totalItem = y.totalItem
})
)
EDIT: Test see here
I want to order some posts by how many times a user has posted a post.
I have the following:
IList<User> orderTopContributors =
this.GetPosts()
.GroupBy(x => x.Author.Id)
.Select(x => new
{
AuthorCount = x.Count()
})
.OrderByDescending( x => x.AuthorCount )
.ToList();
Where am i going wrong? There is an error with the casting:
Severity Code Description Project File Line Error CS0266 Cannot
implicitly convert type 'System.Collections.Generic.List< Items>>' to
'System.Collections.Generic.IList'. An explicit conversion
exists (are you missing a cast?)
tl;dr: Use the SelectMany method
You have few mistakes:
First of all (you fixed this one in an edit), you should use OrderByDescending in order to get the order from the biggest to the smallest.
Next (you fixed this one in an edit), you are expecting to receive IList<User>, either change it to IEnumrable<User> or add .ToList() in the end of your Linq.
Lastly, if you want to flatten your groups to a single list use SelectMany and select your flattened lists:
Example code:
IList<User> orderTopContributors = GetPosts()
.GroupBy(x => x.Id)
.Select(x => new
{
AuthorCount = x.Count(),
Posts = x
})
.OrderByDescending(x => x.AuthorCount)
.SelectMany(x => x.Posts)
.ToList();
When you are using .GroupBy you turn your IEnumerable<User> to IEnumerable<IEnumerable<User>> since there are few groups (many times many), by using the SelectMany method you state which IEnumerable<T> you want to take from each group and aggregate it to the final result:
Example pseudo:
var Users = new List<User>
{
{ UserID = 576, PostId = 7 },
{ UserID = 576, PostId = 4 },
{ UserID = 4, PostId = 2 },
{ UserID = 2, PostId = 5 },
{ UserID = 2, PostId = 1 },
{ UserID = 576, PostId = 9 }
}
var Ordered = Users
.GroupBy(x => x.UserID)
.Select(x => new
{
AuthorCount = x.Count(),
Posts = x
})
.OrderByDescending(x => x.AuthorCount)
.SelectMany(x => x.Posts)
.ToList();
Ordered is now:
List<User>
{
{ UserID = 576, PostId = 7 },
{ UserID = 576, PostId = 4 },
{ UserID = 576, PostId = 9 },
{ UserID = 2, PostId = 5 },
{ UserID = 2, PostId = 1 },
{ UserID = 4, PostId = 2 }
}