Create JSON arrays using LINQ - c#

I have a list of objects returning from the database that look like this when serialized using JSON.NET:
"[{"Percentage":0.78, "PartCode":"D40", "InspectionCode":"292", "Make":"TOYOTA"}]
{"Percentage":0.18, "PartCode":"6N", "InspectionCode":"292", "Make":"GM"},
{"Percentage":0.57, "PartCode":"6N", "InspectionCode":"F", "Make":"GM"},
{"Percentage":0.49, "PartCode":"D40", "InspectionCode":"F", "Make":"TOYOTA"},
{"Percentage":0.09, "PartCode":"785", "InspectionCode":"KB", "Make":"CHRYSLER"},
{"Percentage":0.09, "PartCode":"705", "InspectionCode":"KB", "Make":"FORD"},
{"Percentage":0.18, "PartCode":"D40", "InspectionCode":"KB", "Make":"TOYOTA"},
{"Percentage":0.61, "PartCode":"D40", "InspectionCode":"KB", "Make":"TOYOTA"},
{"Percentage":0.39, "PartCode":"705", "InspectionCode":"SB", "Make":"FORD"},
{"Percentage":0.31, "PartCode":"6N", "InspectionCode":"SB", "Make":"GM"},
{"Percentage":0.21, "PartCode":"AW7", "InspectionCode":"XE1", "Make":"CHRYSLER"},
{"Percentage":0.27, "PartCode":"705", "InspectionCode":"XE1", "Make":"FORD"},
{"Percentage":0.28, "PartCode":"UX", "InspectionCode":"XE1", "Make":"FORD"},
{"Percentage":0.56, "PartCode":"D40", "InspectionCode":"XE1", "Make":"TOYOTA"}]"
I need to create two JSON arrays in this format to pass to HighCharts:
var categories = [
{name: "Toyota", categories: ['D40']},
{name: "GM", categories: ['6N']},
{name: "FORD", categories: ['705', 'UX']},
{name: "CHRYSLER", categories: ['AW7','785']}];
var series = [
{name: "292", data = [0.78, 0.18]}
{name: "F", data = [0.57, 0.49]},
{name: "KB", data = [0.09, 0.09, 0.18, 0.61]},
{name: "SB", data = [0.39, 0.31]},
{name: "XE1", data = [0.21, 0.27, 0.28, 0.56]}];
So far, I am doing the nested grouping of data, since Make and PartCode are hierarchical data.
var query = from d in sortedData
group d by d.Make into newgroup1
from newgroup2 in
(from e in newgroup1
group e by e.PartCode)
group newgroup2 by newgroup1.Key;
I am able to see data in a hierarchical format using:
foreach (var outergroup in query)
{
System.Diagnostics.Debug.WriteLine(outergroup.Key);
foreach (var innergroup in outergroup)
{
System.Diagnostics.Debug.WriteLine(innergroup.Key);
foreach (var innerGroupElement in innergroup)
{
System.Diagnostics.Debug.WriteLine("\t\t{0} {1}", innerGroupElement.InspectionCode, innerGroupElement.Percentage);
}
}
}
But, I am having a hard time understanding what to do further to get to the desired JSON arrays. What steps do I have to take further inorder to achieve this?

This will get you what you want. Once you group by the Make or InspectionCode, then all the items in that sub-list will contain the data you are looking for.
var categories = sortedData.GroupBy(d => d.Make)
.Select(g => new
{
name = g.Key,
categories = g.Select(x => x.PartCode).ToArray()
});
var series = sortedData.GroupBy(d => d.InspectionCode)
.Select(g => new
{
name = g.Key,
data = g.Select(x => x.Percentage).ToArray()
});
var categoriesAsJson = Newtonsoft.Json.JsonConvert.SerializeObject(categories);
var seriesAsJson = Newtonsoft.Json.JsonConvert.SerializeObject(series);
If you have the data already in memory, converting it to Json is as easy as the last two lines, using Json.NET. If you are sending this out over the wire via a WebAPI endpoint, then you can just have your endpoint return a list which would be the categories or series list objects without having converted them to JSON.
[HttpGet]
public HttpResponseMessage GetCategories()
{
var categories = GetCategoriesUsingAboveCode();
return Request.CreateResponse(HttpStatusCode.OK, categories);
}

Something like that?
var categories = sortedData
.GroupBy(i => i.Make)
.Select(g => new
{
name = g.Key,
categories = sortedData
.Where(i2 => i2.Make == g.Key)
.Select(i2 => i2.InspectionCode)
});

Related

OrderBy search results that startswith searcch word then by results that contains search word

I have an object like this:
public class MyObject
{
public List<string> Names { get; set; }
// other props
}
and I have a filtered List like this :
var freeText = "comm";
var list = new List<MyObject>(); // Initialized
var searchResult = list.Where(o =>
o.Names.Any(n => n.ToLower().StartsWith(freeText.ToLower())) ||
o.Names.Any(n => n.ToLower().Contains(freeText.ToLower())));
and it's working fine but, what I'm trying to do is to get the search results ordered by
starts with first then by contains.
ex:
Obj 1 {Names : [ "free communication" ,"some name"]},
Obj 2 { Names : ["communcation Center", "whatever"]}
I want the result to be [Obj2, Obj1].
I tried to orderBy index of freeText but it doesn't seem to work on an array/list of string.
I tried to ulter the solution at This question so it works on array instead of string but it didn't work.
any idea how to to this?
You can implement a simple scoring mechanism where you'll capture two flags (startsWith and contains) and use those flags both for filtering and for sorting:
var result = list.Select(item => new
{
item,
startsWith = item.Names.Any(n => n.ToLower().StartsWith(freeText.ToLower())),
contains = item.Names.Any(n => n.ToLower().Contains(freeText.ToLower())),
})
.Where(item => item.startsWith || item.contains)
.OrderByDescending(item => item.startsWith)
.ThenByDescending(item => item.contains)
.Select(x => x.item);

LINQ Group By and merge sublist of Group back into unique list

I have this class (simplified for easy reading)
public class Customer
{
public string Id {get;set;}
public Email[] Emails {get;set;}
}
From a external system I get a list with Customers that can contain multiple lines for the same Customer (ID)
Raw input JSON
[
{id: a1, emails:[a,b,c]},
{id: a1, emails:[d]},
{id: b3, emails:[e,f]},
{id: k77, emails:[z,a]}
]
c# code to fetch the Customers
List<Customer> dataInput = CallToExternalService(...);
I want to generate a unique list of Customers via LINQ that contains a merged list of all the customers emails.
I know how to get a list of unique customers
dataInput.GroupBy(x => x.id).Select(x => x.First()).ToList();
But I'm struggling with how to merge the email lists into one for each customer. Also performance is an important factor since the data will contain 10k+ items and needs to run every hour.
I tried a lot, Select and SelectMany are good candidates but I cant wrap my head around how to merge lists, not to speak of taking this merged list back to the x.First() item.
META CODE:
dataInput
.GroupBy(x => x.id)
.ForEachGroup(y => group.First().Emails = MergeLists(y.Emails;)
.Select(z => z.First()),ToList();
Expected End result C# List<>
id: a1, emails:[a,b,c,d]
id: b3, emails:[e,f]
id: k77, emails:[z,a]
Making some assumptions about what you mean by "merge", but does this look right?
dataInput
.GroupBy(x => x.Id)
.Select(g=> new Customer
{
Id = g.Key,
Emails = g.SelectMany(c => c.Emails).ToArray()
})
.ToList();
If you're sure that all the other properties are the same, you can use First like in your initial attempt and modify #StriplingWarrior's answer like this:
dataInput
.GroupBy(x => x.Id)
.Select(g =>
{
var customer = g.First();
customer.Emails = g.SelectMany(c => c.Emails).ToArray();
return customer;
})
.ToList();
If you're going to use Jack's approach, I'd suggest something slightly more robust.
var intermediate =
(
from g in dataInput.GroupBy(c => c.Id)
from c in g.Take(1)
select new
{
customer = c,
emails = g.SelectMany(d => d.Emails).ToArray()
}
)
.ToArray();
foreach (var x in intermediate)
{
x.customer.Emails = x.emails;
};
Customer[] ouput =
intermediate
.Select(x => x.customer)
.ToArray();

C# Linq - Refactoring ForEach Loop with Sub List

Am trying to refactor some data in order to display some charts.
I can't seem to figure out why using the following, it lists all the values at the top rather than being sequential like the source data.
var categories = VehicleSales.Select(v => v.name).Distinct().ToList();
var refactoredResults = new List<StackedColumnChart>();
foreach (var category in categories)
{
var subresult = VehicleSales.Where(x => x.vehicleType == category)
.GroupBy(x => x.vehicleType)
.Select(gcs => new StackedColumnChart
{
Category = category,
Values = gcs.Select(x => (int)x.data).DefaultIfEmpty(0).ToList()
}).ToList();
refactoredResults.AddRange(subresult);
}
Source Data:
Then the actual results and expected results:
Thanks in advance!
You can do that without loop and selecting a distinct values, just use GroupBy method and map each group to StackedColumnChart using Select
var refactoredResults = VehicleSales
.GroupBy(s => s.Category)
.Select(g => new StackedColumnChart
{
Category = g.Key,
Values = g.Select(s => s.Value).ToList()
})
.ToList();
If the original data is not sorted and you'll need to sort the values by week number, you can use OrderBy clause before selecting a values Values = g.OrderBy(s => s.WeekNumber).Select(s => s.Value).ToList()

Serializing to JSON

this is my sql result
and i want to serialize the result to json
like following
{
"Studentid": 1000,
"ExamType":[
{
"Examtype":"TERM 2",
"ExamName":[{
"ExamName":"PERIODIC TEST 1-Term2",
"SubjectName": [{
"SubjectName":"SL-MALAYALAM",
"ComponentName":[{
"ComponenetName":"Exam",
"SubComponent":[{
"SubCOmponent":"Exam",
"ExamDate":"2017-08-03",
"MaxMark":"50.00",
"MarkObtained":"38.00",
"Grade":"B1"
}]
},
{
"ComponenetName":"NOTEBOOK",
"SubComponent":[{
"SubCOmponent":"Neatness & upkeep",
"ExamDate":"2017-08-03",
"MaxMark":"2.00",
"MarkObtained":"2.00" ,
"Grade":"A1"
}]
}]
}]
}]
}]
}
how can i serialize the sql result to json in mvc api,i'm already using newtonsoft for serializing other results,using LINQ is better way? if yes how?
my code Looks like
I don't know of any libraries that will do it for you automatically, but you can certainly write code that does something like:
var grouped = results.GroupBy(r => r.StudentID).Select(g => new
{
StudentID = g.Key,
ExamTypes = g.GroupBy(r => r.ExamType).Select(g2 => new
{
ExamType = g2.Key,
ExamNames = g2.GroupBy(r => r.ExamName).Select(g3 => new
{
ExamName = g3.Key,
SubjectNames = g3.GroupBy(r => r.SubjectName).Select(g4 => new
{
SubjectName = g4.Key,
SubComponents = g4.Select(r => new { SubjectComponentName = r.SubjectComponentName, ExamDate = r.ExamDate, MaxMark = r.MaxMark, MarkObtained = r.MarkObtained /* others here */ })
})
})
})
});
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(grouped);
Note that I've pluralized the names of the properties whose values are collections, but if you're required to keep to the exact property names specified, you can change that easily enough.

How do i sum a list of items by code(or any field)?

I have an object that has a list of another object in it. i.e Object1 contains List<Object2>.
Assuming this is the definition of object 2:
public class Object2
{
string code,
string name,
decimal amount
}
I want to be a able to make a list2 from the list whose value will contain what something similar to what a select name, code, sum(amount) group by code kinda statement could have given me
this is what i did but it didnt contain what i needed on passing through.
var newlist = obj2List.GroupBy(x => x.code)
.Select(g => new { Amount = g.Sum(x => x.amount) });
I want code and name in the new list just like the sql statement above.
You're almost there:
var newlist = obj2List.GroupBy(x => x.code)
.Select(g => new
{
Code = g.First().code,
Name = g.First().name,
Amount = g.Sum(x => x.amount)
});
This groups the items by code and creates an anonymous object for each group, taking the code and name of first item of the group. (I assume that all items with the same code also have the same name.)
If you are grouping by code and not by name you'd have to choose something for name from the list, perhaps with First() or Last() or something.
var newlist = obj2List.GroupBy(x => x.code).Select(g => new {
Code = g.Key,
Name = g.First().name,
Amount = g.Sum(x => x.amount)
});
var query = Object1.Obj2List
.GroupBy(obj2 => obj2.code)
.Select(g => new {
Names = string.Join(",", g.Select(obj2.name)),
Code = g.Key,
Amount = g.Sum(obj2 => obj2.Amount)
});
Since you group by code only you need to aggregate the name also in some way. I have used string.Join to create a string like "Name1,Name2,Name3" for each code-group.
Now you could consume the query for example with a foreach:
foreach(var x in query)
{
Console.WriteLine("Code: {0} Names: {1} Amount: {2}"
, x.Code, x.Names, x.Amount);
}
Instead of using the LINQ Extension Methods .GroupBy() and .Select() you could also use a pure LINQ statement which is way easier to read if you come from a SQL Background.
var ls = new List<Object2>();
var newLs = from obj in ls
group obj by obj.code into codeGroup
select new { code = codeGroup.Key, amount = codeGroup.Sum(s => s.amount) };

Categories

Resources