I have two lists,
private List<DealResponse> L1 = new List<DealResponse>
{
new DealResponse {detailId = "5", detailcd = "ABC", fileName = "string 1", isNgo = "0"},
new DealResponse {detailId = "5", detailcd = "DEF", fileName = "string 2", isNgo = "0"},
new DealResponse {detailId = "5", detailcd = "XYZ", fileName = "string ", isNgo = "0"}
};
private List<DealResponse> L2 = new List<DealResponse>
{
new DealResponse {detailId = "5", detailcd = "ABC", fileName = "string 11", isNgo = "1"},
new DealResponse {detailId = "6", detailcd = "MNO", fileName = "string 3", isNgo = "1"}
};
I'm trying to write a method which accepts detailId and return the result by merging above two list, and if there is duplicate (duplicate definition = when detailId, detailcd match between two lists) select entry from L2
so after merging result would be
var Result = new List<DealResponse>
{
new DealResponse {detailId = "5", detailcd = "ABC", fileName = "string 11", isNgo = "1"},
new DealResponse {detailId = "5", detailcd = "DEF", fileName = "string 2", isNgo = "0"},
new DealResponse {detailId = "5", detailcd = "XYZ", fileName = "string ", isNgo = "0"},
new DealResponse {detailId = "6", detailcd = "MNO", fileName = "string 3", isNgo = "1"},
};
Note that in result we selected this entry from L2 since detailId = 5, detailcd = ABC was duplicate
public List<DealResponse> GetDealResponse(string detailId)
{
// My main confusion is while doing union how to handle the case
// which I mentioned (On duplicate select entry from second list)
var L3 = L1.Union(L2, new DealResponseComprarer()).ToList();
}
public class DealResponse
{
public string detailId { get; set; }
public string detailcd { get; set; }
public string fileName { get; set; }
public string isNgo { get; set; }
}
public class DealResponseComprarer : IEqualityComparer<DealResponse>
{
public bool Equals(DealResponse x, DealResponse y)
{
return x.detailId == y.detailId && x.detailcd == y.detailcd ;
}
public int GetHashCode(DealResponse obj)
{
return (obj.detailId.GetHashCode() + obj.detailcd.GetHashCode());
}
}
In case you actually want to filter your results using detailId, since this value is passed to the GetDealResponse() method, you could add a .Where condition to the equalized Union list.
public class DealResponse
{
public string detailId { get; set; }
public string detailcd { get; set; }
public string fileName { get; set; }
public string isNgo { get; set; }
}
public List<DealResponse> GetDealResponse(string detailId)
{
return L2.Union(L1, new DealResponseComprarer())
.Where(elm => elm.detailId.Equals(detailId)).ToList();
}
L1 = new List<DealResponse>() {
new DealResponse() { detailId = "5", detailcd = "ABC" , fileName = "string 1", isNgo = "0" },
new DealResponse() { detailId = "5", detailcd = "DEF" , fileName = "string 2", isNgo = "0" },
new DealResponse() { detailId = "5", detailcd = "XYZ" , fileName = "string ", isNgo = "0" }};
L2 = new List<DealResponse>() {
new DealResponse() { detailId = "5", detailcd = "ABC" , fileName = "string 11", isNgo = "1" },
new DealResponse() { detailId = "6", detailcd = "MNO" , fileName = "string 3", isNgo = "1" }};
string ID = "5";
List<DealResponse> L3 = GetDealResponse(ID);
Which would return this list:
{ detailId = "5", detailcd = "ABC" , fileName = "string 11", isNgo = "1" }
{ detailId = "5", detailcd = "DEF" , fileName = "string 2", isNgo = "0" }
{ detailId = "5", detailcd = "XYZ" , fileName = "string ", isNgo = "0" }
I'm trying to write a method which accepts detailId and return the result by merging above two list, and if there is duplicate (duplicate definition = when detailId, detailcd match between two lists) select entry from L2
Another way to say this is:
Start with L2
Add everything from L1 that isn't already in L2
This can be accomplished with one line:
var combined = L2.Concat(L1.Except(L2, new DealResponseComprarer()));
Example on DotNetFiddle
Related
I have the following DTO:
class Permission
{
public string ObjectId { get; set; }
public string ObjectType { get; set; }
public string Scope { get; set; }
public string? AccountId { get; set; }
public string? GroupId { get; set; }
}
var permissions = new List<Permission>()
{
new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = "1", GroupId = null },
new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = "2", GroupId = null },
new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = null, GroupId = "1" },
new(){ ObjectId = "1", ObjectType = "link", Scope = "link:write", AccountId = "2", GroupId = null },
new(){ ObjectId = "2", ObjectType = "link", Scope = "link:read", AccountId = "1", GroupId = null },
};
I want the following outcome:
{[
"1": {
"read": {
"accounts": ["1", "2"],
"groups": ["1", "2", "3"]
},
"write": {
"accounts": ["1", "2"],
"groups": ["1", "2", "3"]
}
}
]}
Basically an array of objects of Permission, grouped by their ObjectId property, and then grouped by each Scope that contains an array of the AccountId or GroupId properties.
Each Permission can have the same ObjectId but different Scope, AccountId and GroupId.
I tried to use GroupBy but that gives me an IGrouping and I'm not sure how to proceed.
You need more than just nested group bys. In your output JSON you have keys as property names. You also need to use ToDictionary to convert values into property keys.
var permissions = new List<Permission>()
{
new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = "1", GroupId = null },
new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = "2", GroupId = null },
new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = null, GroupId = "1" },
new(){ ObjectId = "1", ObjectType = "link", Scope = "link:write", AccountId = "2", GroupId = null },
new(){ ObjectId = "2", ObjectType = "link", Scope = "link:read", AccountId = "1", GroupId = null },
};
var result = permissions.GroupBy(r=> r.ObjectId).Select(r=> new {
r.Key,
InnerGroups = r.GroupBy(q=> q.Scope.Replace("link:","")).Select(q=> new {
Scope = q.Key,
Accounts = q.Where(z=> z.AccountId != null).Select(z=> z.AccountId).ToArray(),
Groups = q.Where(z=> z.GroupId != null).Select(z=> z.GroupId).ToArray()
})
})
.ToDictionary(r=> r.Key,r=> r.InnerGroups.ToDictionary(q=> q.Scope,q=> new {q.Accounts,q.Groups}));
var serialized = JsonSerializer.Serialize(result,new JsonSerializerOptions{ WriteIndented=true });
Here is the output :
{
"1": {
"read": {
"Accounts": [
"1",
"2"
],
"Groups": [
"1"
]
},
"write": {
"Accounts": [
"2"
],
"Groups": []
}
},
"2": {
"read": {
"Accounts": [
"1"
],
"Groups": []
}
}
}
Fiddle
You would have to use nested GroupBy to achieve that and then convert the result to the dictionaries:
var result = permissions
.GroupBy(p => p.ObjectId)
.ToDictionary(g => g.Key, g => g.GroupBy(g => g.Scope)
.ToDictionary(g => g.Key, g => new
{
accounts = g.Select(per => per.AccountId).Distinct().ToList(),
groups = g.Select(per => per.GroupId).Distinct().ToList()
}));
I have a c# class that looks something like this:
public class Item
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Type { get; set; }
}
and i would like to convert it to json where json property name is item name and its value is item description. if some item has any children then i would like the json property name to stay as item name and the children to be added like item name:item description(the parent item description and type become empty string if it has any children, except when the type is array). The type has following values: array, string, int, object. if the item type is an array then each time a child is added to the type array item, the child is item description. so i would like those values to be added to the json array as well.
if the type is string or int the json property value should be int or string.
I am trying to write custom JsonSerializer but i am getting nowhere.
so if i have a list with items:
List<Item> MyItems = new List<Item>()
{
new Item { Id = 1, ParentId = null, Name = "Name1", Description = "", Type = "" },
new Item { Id = 2, ParentId = 1, Name = "Name2", Description = "Description1", Type = "String" },
new Item { Id = 3, ParentId = 1, Name = "Name3", Description = "", Type = "Array" },
new Item { Id = 4, ParentId = 3, Name = "", Description = "ArrayItem1", Type = "" },
new Item { Id = 5, ParentId = 3, Name = "", Description = "ArrayItem2", Type = "" },
new Item { Id = 6, ParentId = 3, Name = "", Description = "ArrayItem3", Type = "" },
new Item { Id = 7, ParentId = null, Name = "Name4", Description = "5", Type = "Int" },
};
then the json should like this:
{
"name1":{
"name2":"description1",
"name3":[
"ArrayItem1",
"ArrayItem2",
"ArrayItem1"
]
},
"name4":5
}
Here's an extension method that I think does what you want:
public static class Ex
{
public static string ToJson(this List<Item> items)
{
var lookup = items.ToLookup(x => x.ParentId);
JObject ToJson(int? parentId)
{
JProperty ToProperty(Item item)
{
switch (item.Type)
{
case "":
return new JProperty(item.Name, ToJson(item.Id));
case "String":
return new JProperty(item.Name, item.Description);
case "Array":
return new JProperty(item.Name, lookup[item.Id].Select(x => x.Description).ToArray());
case "Int":
return new JProperty(item.Name, int.Parse(item.Description));
default:
return new JProperty(item.Name);
}
}
return new JObject(lookup[parentId].Select(x => ToProperty(x)));
}
var output = ToJson(null);
var text = Newtonsoft.Json.JsonConvert.SerializeObject(output, Newtonsoft.Json.Formatting.Indented);
return text;
}
}
I run it like this:
List<Item> MyItems = new List<Item>()
{
new Item { Id = 1, ParentId = null, Name = "Name1", Description = "", Type = "" },
new Item { Id = 2, ParentId = 1, Name = "Name2", Description = "Description1", Type = "String" },
new Item { Id = 3, ParentId = 1, Name = "Name3", Description = "", Type = "Array" },
new Item { Id = 4, ParentId = 3, Name = "", Description = "ArrayItem1", Type = "" },
new Item { Id = 5, ParentId = 3, Name = "", Description = "ArrayItem2", Type = "" },
new Item { Id = 6, ParentId = 3, Name = "", Description = "ArrayItem3", Type = "" },
new Item { Id = 7, ParentId = null, Name = "Name4", Description = "5", Type = "Int" },
};
Console.WriteLine(MyItems.ToJson());
And I get this out:
{
"Name1": {
"Name2": "Description1",
"Name3": [
"ArrayItem1",
"ArrayItem2",
"ArrayItem3"
]
},
"Name4": 5
}
I've managed to implement $inlinecount with WebApi.OData (v 4.0.0) using the ODataQueryOptions<T> and PageResult<T> classes like this:
POCO
public class Poco
{
public int id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
Controller
[ActionName("Default")]
public PageResult<Poco> Get(ODataQueryOptions<Poco> queryOptions)
{
var data = new Poco[] {
new Poco() { id = 1, name = "one", type = "a" },
new Poco() { id = 2, name = "two", type = "b" },
new Poco() { id = 3, name = "three", type = "c" },
new Poco() { id = 4, name = "four", type = "d" },
new Poco() { id = 5, name = "five", type = "e" },
new Poco() { id = 6, name = "six", type = "f" },
new Poco() { id = 7, name = "seven", type = "g" },
new Poco() { id = 8, name = "eight", type = "h" },
new Poco() { id = 9, name = "nine", type = "i" }
};
var t = new ODataValidationSettings() { MaxTop = 2 };
queryOptions.Validate(t);
var s = new ODataQuerySettings() { PageSize = 1 };
IQueryable results = queryOptions.ApplyTo(data.AsQueryable(), s);
var next = Request.GetNextPageLink();
var count = Request.GetInlineCount();
return new System.Web.Http.OData.PageResult<Poco>(
results as IEnumerable<Poco>, next, count);
}
I'm getting error 406 when I switch from JSON to old school XmlSerializer. Does anyone know if this should work?
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
GlobalConfiguration.Configuration.Formatters.Remove(
GlobalConfiguration.Configuration.Formatters.JsonFormatter);
PageResult can't be serialized by XmlSerializer because it doesn't have a public, parameterless constructor. But there's nothing stopping you from defining your own similar type that does have a public, parameterless constructor. It should be pretty simple to do. I'd recommend taking a look at the source code for PageResult and adopting a similar approach.
In my MVC3 application I want to create an anonymous collection with fields names like this:
new
{
Buyer.Firstname = "Jim",
Buyer.Lastname = "Carrey",
Phone = "403-222-6487",
PhoneExtension = "",
SmsNumber = "",
Buyer.Company = "Company 10025",
Buyer.ZipCode = "90210",
Buyer.City = "Beverly Hills",
Buyer.State = "CA",
Buyer.Address1 = "Address 10025"
Licenses[0].IsDeleted = "False",
Licenses[0].ID = "6",
Licenses[0].AdmissionDate = "2,1999",
Licenses[0].AdmissionDate_monthSelected = "2",
}
I want to have this in order to send custom post requests during integration testing of my app. How can I declare a an anonymous collection with this field names?
Use an anonymous collection of anonymous objects, like so:
Licenses = new [] {
new {
IsDeleted = "False",
ID = "6",
AdmissionDate = "2,1999",
AdmissionDate_monthSelected = "2"
} //, ... and so on
}
... and in context: ([edit] Oh, and I didn't see your buyer...)
new
{
Buyer = new {
Firstname = "Jim",
Lastname = "Carrey",
Company = "Company 10025",
ZipCode = "90210",
City = "Beverly Hills",
State = "CA",
Address1 = "Address 10025",
},
Phone = "403-222-6487",
PhoneExtension = "",
SmsNumber = "",
Licenses = new [] {
new {
IsDeleted = "False",
ID = "6",
AdmissionDate = "2,1999",
AdmissionDate_monthSelected = "2"
}
}
}
You could use object and collection initializer syntax:
var anonymousObject = new
{
Phone = "403-222-6487",
PhoneExtension = "",
SmsNumber = "",
Buyer = new
{
Firstname = "Jim",
Lastname = "Carrey",
Company = "Company 10025",
ZipCode = "90210",
City = "Beverly Hills",
State = "CA",
Address1 = "Address 10025"
},
Licenses = new[]
{
new
{
IsDeleted = "False",
ID = "6",
AdmissionDate = "2,1999",
AdmissionDate_monthSelected = "2",
}
}
}
Try this:
var x = new {
Phone = "403-222-6487",
PhoneExtension = "",
SmsNumber = "",
Buyer = new {
Firstname = "Jim",
Lastname = "Carrey",
Company = "Company 10025",
ZipCode = "90210",
City = "Beverly Hills",
State = "CA",
Address1 = "Address 10025"
},
Licenses = new[] {
new {
IsDeleted = "False",
ID = "6",
AdmissionDate = "2,1999",
AdmissionDate_monthSelected = "2"},
new {
IsDeleted = "True",
ID = "7",
AdmissionDate = "17,2001",
AdmissionDate_monthSelected = "3"}
}
};
Note: I am using a nested anonymous type for buyers and a nested array of yet another anyonymous type for licences. This allows you to access values like this
string name = x.Buyer.Lastname;
string id = x.Licences[0].ID;
I have some problem here. Here it is:
I have this class
public class NewsFeedResources
{
public string Name { get; set; }
public string Id { get; set; }
public string Message { get; set; }
public static ObservableCollection<NewsFeedResources> _newsfeed = new ObservableCollection<NewsFeedResources>
{
new NewsFeedResources { Name = "Joe", Id = "1", Message="Foo" },
new NewsFeedResources { Name = "Wandy", Id = "2", Message="Bar" },
new NewsFeedResources { Name = "Yuliana", Id = "3", Message="Baz" },
new NewsFeedResources { Name = "Hardi", Id = "4", Message="Baz" },
};
public static ObservableCollection<NewsFeedResources> newsFeedResources
{ get { return _newsfeed; }
}
}
If I have another data such as
Name=John, Id=5, Message="Stack overflow"
Name=Jane, Id=6, Message="Hello world"
How can I add the data into the class, but not from the constructor? Thanks for the help
ObservableCollection exposes the Collection<T>.Add Method:
Adds an object to the end of the Collection.
So you'd have:
_newsfeed.Add(new NewsFeedResources {Name = "John",
Id = 5,
Message = "Stack overflow"});
_newsfeed.Add(new NewsFeedResources {Name = "Jane",
Id = 6,
Message = "Hello world"});
(typed from memory)
call a function from constructor or anywhere as u like and add items like below
NewsFeedResources NFR=new NewsFeedResources(){Name=John, Id=5, Message="Stack overflow"};
_newsfeed.add(NFR);
NewsFeedResources NFR1 =new NewsFeedResources(){Name=Jane, Id=6, Message="Hello world"};
_newsfeed.add(NFR);