C# Linq how to use group by to get the desired output - linq

I have the following classes
public class Product
{
public string PNo {get;set;}
public String GCode {get;set;}
public IList<Detail> Details {get;set;}
}
public class Detail{
public string Color {get;set;}
public string Size {get;set;}
}
And i have the data in an array as below
PNO GCode Color Size Amount
12345 GCC A L 1
12345 GCC V M 2
12345 GCC C S 3
12345 GCC D XL 4
12345 GCC E XS 5
How do i get the following output using groupby so that my output could like shown below
Expected output:
{
PNO: 12345,
GCode: GCC,
options:[
{Color:A, Size:L, Amount: 1},
{Color:V, Size:M, Amount: 2},
{Color:C, Size:S, Amount: 3},
{Color:D, Size:XL, Amount: 4},
{Color:E, Size:XS, Amount: 5}
]
}
Thanks

Given the contract you defined. I think below code snippet is OK to generate the JSON you want, I use anonymous object a lot, you can replace with you entity class.
products.GroupBy(product => new
{
product.PNo,
product.GCode
})
.Select(productGroup => new
{
productGroup.Key.PNo,
productGroup.Key.GCode,
options = productGroup.SelectMany(product => product.Details.Select(detail => new
{
detail.Color,
detail.Size,
detail.Amount
}))
});
Hope it helps.

This will get you that expected output:
Query Syntax:
var result = (from item in collection
group item by new { item.PNO, item.GCode } into grouping
select new Product
{
PNo = grouping.Key.PNO,
GCode = grouping.Key.GCode,
Details = grouping.Select(item => new Detail { Color = item.Color, Size = item.Size, Amount = item.Amount }).ToList()
}).ToList();
Method Syntax:
var result = collection.GroupBy(item => new { item.PNO, item.GCode })
.Select(grouping => new Product
{
PNo = grouping.Key.PNO,
GCode = grouping.Key.GCode,
Details = grouping.Select(item => new Detail { Color = item.Color, Size = item.Size, Amount = item.Amount }).ToList()
}).ToList();
Test Data:
List<dynamic> collection = new List<dynamic>()
{
new { PNO = "12345", GCode = "GCC", Color = "A", Size="L", Amount=1 },
new { PNO = "12345", GCode = "GCC", Color = "V", Size="M", Amount=2 },
new { PNO = "12345", GCode = "GCC", Color = "C", Size="S", Amount=3 },
new { PNO = "12345", GCode = "GCC", Color = "D", Size="XL", Amount=4 },
new { PNO = "12345", GCode = "GCC", Color = "E", Size="XS", Amount=5 },
};

Related

How to get attributes from OData response via AJAX?

I'm working on the MVC application which using OData & Web API via ajax. I'm trying to do paging from server side by using OData filter attributes. Here is my code of Controller.
[RoutePrefix("OData/Products")]
public class ProductsController : ODataController
{
private List<Product> products = new List<Product>
{
new Product() { Id = 1, Name = "Thermo King MP-3000", Price = 300, Category = "Thermo King" },
new Product() { Id = 2, Name = "Thermo King MP-4000", Price = 500, Category = "Thermo King" },
new Product() { Id = 3, Name = "Daikin Decos III c", Price = 200, Category = "Daikin" },
new Product() { Id = 4, Name = "Daikin Decos III d", Price = 400, Category = "Daikin" },
new Product() { Id = 5, Name = "Starcool RCC5", Price = 600, Category = "Starcool" },
new Product() { Id = 6, Name = "Starcool SCC5", Price = 700, Category = "Starcool" }
};
[EnableQuery(PageSize=2)]
public IQueryable<Product> Get()
{
return products.AsQueryable<Product>();
}
//[EnableQuery]
//public SingleResult<Product> Get([FromODataUri] int id)
//{
// var result = products.Where(x => x.Id.Equals(id)).AsQueryable();
// return SingleResult.Create<Product>(result);
//}
[EnableQuery]
public Product Get([FromODataUri] int id)
{
return products.First(x => x.Id.Equals(id));
}
}
And here is my code of javascript:
<script type="text/javascript">
$(document).ready(function () {
var apiUrl = "http://localhost:56963/OData/Products";
$.getJSON(apiUrl,
function (data) {
$("#div_content").html(window.JSON.stringify(data));
}
);
//$.get(apiUrl,
//function (data) {
// alert(data[0]);
// $("#div_content").html(data);
//});
});
</script>
The response from OData is JSON result like:
{"#odata.context":"http://localhost:56963/OData/$metadata#Products","value":[{"Id":1,"Name":"Thermo King MP-3000","Price":300,"Category":"Thermo King"},{"Id":2,"Name":"Thermo King MP-4000","Price":500,"Category":"Thermo King"}],"#odata.nextLink":"http://localhost:56963/OData/Products?$skip=2"}
I was trying to get "#odata.nextLink" but failed, there is no way to get "odata.nextLink" by "data.#odata.nextLink" from javascript.
Any one can help me to get through this?
After parse the string to json, data['#odata.nextLink'] can work:
var data = '{"#odata.context":"http://localhost:56963/OData/$metadata#Products","value":[{"Id":1,"Name":"Thermo King MP-3000","Price":300,"Category":"Thermo King"},{"Id":2,"Name":"Thermo King MP-4000","Price":500,"Category":"Thermo King"}],"#odata.nextLink":"http://localhost:56963/OData/Products?$skip=2"}';
data = JSON.parse(data);
alert(data['#odata.nextLink']);

Use a Linq statement to perform outer join

Starting with the collection below, what Linq statement do I need to return a results set that satisfies the test?
private List<dynamic> _results;
[SetUp]
public void SetUp()
{
_results = new List<dynamic>
{
new {Id = 1, Names = new[] {"n1"}, Tags = new[] {"abc", "def"}},
new {Id = 2, Names = new[] {"n2", "n3"}, Tags = new[] {"ghi"}},
new {Id = 3, Names = new[] {"n1", "n3"}, Tags = new[] {"def", "xyz"}},
new {Id = 4, Names = new[] {"n4"}, Tags = new string[] {}}
};
}
private ILookup<string, string> GetOuterJoinedCollection(IEnumerable<dynamic> results)
{
// ???
}
[Test]
public void Test()
{
ILookup<string, string> list = GetOuterJoinedCollection(_results);
Assert.That(list.Count, Is.EqualTo(4));
Assert.That(list["n1"], Is.EquivalentTo(new [] { "abc", "def", "def", "xyz" }));
Assert.That(list["n2"], Is.EquivalentTo(new [] { "ghi" }));
Assert.That(list["n3"], Is.EquivalentTo(new [] { "ghi", "def", "xyz" }));
Assert.That(list["n4"], Is.EquivalentTo(new string[] { }));
}
Note: this is a follow up from a previous question: Convert Lambda into Linq Statement with nested for loop

LINQ Grouping or Splitting

Im having an issue doing something thats probably pretty simple.
My LINQ query is returning a result set of 10 objects.
Something like:
Name: Bill, Action: aaa, Id: 832758
Name: Tony, Action: aaa, Id: 82fd58
Name: Bill, Action: bbb, Id: 532758
Name: Tony, Action: bbb, Id: 42fd58
What I need to do, is to group these so that there are only 2 rows, ie one per Name, but have the ones with "Action: bbb" move into a different column. So the output would be:
Name: Bill, Action: aaa, Action_2: bbb, Id: 832758, Id_2: 532758
Name: Tony, Action: aaa, Action_2: bbb, Id: 82fd58, Id_2: 42fd58
Can anyone explain to me how I might do that?
Cheers
var myData = new []
{
new { Name = "Bill", Action="aaa", Id = "832758" },
new { Name = "Tony", Action="aaa", Id = "82fd58" },
new { Name = "Bill", Action="bbb", Id = "532758" },
new { Name = "Tony", Action="bbb", Id = "42fd58" }
};
var result = myData
.GroupBy(x=>x.Name)
.Select(g=>new
{
Name = g.Key,
Action = g.First().Action,
Action_2 = g.Last().Action,
Id = g.First().Id,
Id_2 = g.Last().Id
});
The query above assumes that you have only two rows for each name; if you have multiple rows per name, you can take the second Action/Id value by using .Skip(1).Take(1) instead of .Last()
I don't think there's a real simple way to do it. I've connocted this, though, which might get you started:
var myData = new []
{
new { Name = "Bill", Action="aaa", Id = "832758" },
new { Name = "Tony", Action="aaa", Id = "82fd58" },
new { Name = "Bill", Action="bbb", Id = "532758" },
new { Name = "Tony", Action="bbb", Id = "42fd58" }
};
// group all the Names together
var result = from m in myData
group m by m.Name into names
orderby names.Key
select names;
// go through each Name group and create the output string to store in sbLines
var sbLines = new StringBuilder();
foreach (var name in result)
{
var sb = new StringBuilder();
sb.AppendFormat("Name: {0}, ", name.Key);
int count = 1;
foreach (var item in name)
{
if(count > 1)
sb.AppendFormat("Action_{0}: {1}, ", count, item.Action);
else
sb.AppendFormat("Action: {0}, ", item.Action);
count++;
}
count = 1;
foreach (var item in name)
{
if(count > 1)
sb.AppendFormat("Id_{0}: {1}, ", count, item.Id);
else
sb.AppendFormat("Id: {0}, ", item.Id);
count++;
}
sbLines.Append(sb.ToString().Trim(new char[] { ' ',',' }));
sbLines.Append(Environment.NewLine);
}
Console.WriteLine(sbLines.ToString());
Run it here: http://ideone.com/8UTxr

How to project simple data set into to anonymous type of a different shape

I have a simple set of data that I am having trouble figuring out how to create the projection I want using LINQ.
public class Score {
public string Name { get; set; }
public int Value { get; set; }
}
var scores = new List<Score> {
new Score { Name = "jesse", Value = 10 },
new Score { Name = "jesse", Value = 12 },
new Score { Name = "jesse", Value = 15 },
new Score { Name = "billy", Value = 5 },
new Score { Name = "billy", Value = 7 },
new Score { Name = "billy", Value = 20 },
new Score { Name = "colin", Value = 25 },
new Score { Name = "colin", Value = 13 },
new Score { Name = "colin", Value = 8 }
};
I need to project 'scores' into an anonymous type with the following structure.
{
series : [
{ name : "jesse", data : [10, 12, 15 ] },
{ name : "billy", data : [ 5, 7, 20 ] },
{ name : "colin", data : [25, 13, 8 ] }
]
}
Any help is appreciated.
var result = new {
series = from score in scores
group score.Value by score.Name into nameValues
select new
{
name = nameValues.Key,
data = nameValues
}
};
Does this match the structure you want?
var query = scores.GroupBy(s => s.Name);
var result = query.Select(q => new {
Name = q.Key,
Data = q.ToArray().Select(k => k.Value)
});
var anotherAnon = new {series = result.ToArray()};

Grouping data with sum

I have the following list:
mat.Add(new Material() { ID = 1, ProdName = "Cylinder", Weight=23, Discontinued='N' });
mat.Add(new Material() { ID = 2, ProdName = "Gas", Weight = 25, Discontinued='N' });
mat.Add(new Material() { ID = 3, ProdName = "Match", Weight = 23, Discontinued='N' });
I want a result as:
2 Products have Weight 23 and Discontinued N
1 Product have Weigth 25 Discontinued N
It's not clear exactly what you're actually grouping by - is it both weight and the discontinued status? If so, you can just do:
var query = mat.GroupBy(material =>
new { material.Weight, material.Discontinued });
foreach (var result in query)
{
Console.WriteLine("{0} products have {1}", result.Count(), result.Key);
}
"just" group by Weight and Discontinued.
something like :
var result = mat.GroupBy(a=>new{a.Weight,a.Discontinued}).Select(b=>new b.Key.Weight,b.Key.Discontinued,Count = b.Count()});
Got it!
.GroupBy(re => new { re.Weight, re.Discontinued })
.Select(grp => new { CXXX = group.Key, Count = grp.Count()

Resources