I got the requirement to serialize my C# class RoomType like the below
public class RoomType
{
public string name { get; set; }
public string url { get; set; }
public string desc { get; set; }
}
to the below json format like this
"room_types" :
{
"Fenway Room" :
{
"url" : "http://www.partner-site.com/hotel_commonwealth/fenway_room",
"desc" : "One king bed with pillowtop mattress, Frette Italian linens, down bedding, multiple pillows. View of Fenway Park."
},
"Commonwealth Room" :
{
"url" : "http://www.partner-site.com/hotel_commonwealth/commonwealth_room",
"desc" : "One king bed with pillowtop mattress, Frette Italian linens, down bedding, multiple pillows. View of Commonwealth Avenue."
}
}
How to get the "Fenway Room" and "Commonwalth Room" to in this json format?
I tried the suggestion but still can't get how the anonymous fit into what i needed in the ActionResult. Here's my not working code now:
var rooms = new List<HarRoomType>()
{
new HarRoomType()
{
}
};
var anonymous = new
{
type = rooms.ToDictionary(x => x.name, x => new {x.currency, x.discounts})
};
var response = new HotelAvailabilityResponse()
{
api_version = 4,
hotel_ids = new List<int>()
{
1,
2
},
start_date = "2014-02-21",
hotels = new List<HarHotel>()
{
new HarHotel()
{
hotel_id = 1,
room_types = anonymous
},
new HarHotel()
{
hotel_id = 2,
room_types = new List<HarRoomType>()
}
}
};
return Json(response, JsonRequestBehavior.AllowGet);
You need to shape the data as a dictionary:
RoomType[] rooms = ...
var serializeThis = new {
room_types = rooms.ToDictionary(
x => x.name,
x => new { x.url, x.desc }
)
};
JavaScriptSerializer js = new JavaScriptSerializer();
List<RoomType> roomTypes = new List<RoomType>(){
new RoomType{ desc="desc 1", name="Fenway Room", url="blah.com"},
new RoomType{ desc="desc 2", name="Commonowealth Room", url="blah.com"},
};
If you don't care about having the name show up as a property, then:
var json = js.Serialize(roomTypes.ToDictionary(x => x.name));
If you do care about having the name show up and don't want it to:
var json2 = js.Serialize(roomTypes.ToDictionary(x => x.name, x => new { desc = x.desc, url = x.url }));
Use this:
var anonymous= new {
type= rooms.ToDictionary(
x => x.name,
x => new { x.url, x.desc }
)
Related
In this contrived example, which closely resembles my real-world problem, I have a data set coming from an external source. Each record from the external source takes the following form:
[Classification] NVARCHAR(32),
[Rank] INT,
[Data] NVARCHAR(1024)
I am looking to build an object where the Rank and Data are patched into a single instance of a response object that contains list properties for the three hard-coded Classification values, ordered by Rank.
I have something that works, but I can't help but think that it could be done better. This is what I have:
public static void Main()
{
IEnumerable<GroupingTestRecord> records = new List<GroupingTestRecord>
{
new GroupingTestRecord { Classification = "A", Rank = 1, Data = "A1" },
new GroupingTestRecord { Classification = "A", Rank = 2, Data = "A2" },
new GroupingTestRecord { Classification = "A", Rank = 3, Data = "A3" },
new GroupingTestRecord { Classification = "B", Rank = 1, Data = "B1" },
new GroupingTestRecord { Classification = "B", Rank = 2, Data = "B2" },
new GroupingTestRecord { Classification = "B", Rank = 3, Data = "B3" },
new GroupingTestRecord { Classification = "C", Rank = 1, Data = "C1" },
new GroupingTestRecord { Classification = "C", Rank = 2, Data = "C2" },
new GroupingTestRecord { Classification = "C", Rank = 3, Data = "C3" },
};
GroupTestResult r = new GroupTestResult
{
A = records.Where(i => i.Classification == "A").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
B = records.Where(i => i.Classification == "B").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
C = records.Where(i => i.Classification == "C").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
};
The source record DTO:
public class GroupingTestRecord
{
public string Classification { get; set; }
public int? Rank { get; set; }
public string Data { get; set; }
}
The destination single class:
public class GroupTestResult
{
public IEnumerable<GroupTestResultItem> A { get; set; }
public IEnumerable<GroupTestResultItem> B { get; set; }
public IEnumerable<GroupTestResultItem> C { get; set; }
}
The distination child class:
public class GroupTestResultItem
{
public int? Rank { get; set; }
public string Data { get; set; }
}
Ouput
{
"A":[
{
"Rank":1,
"Data":"A1"
},
{
"Rank":2,
"Data":"A2"
},
{
"Rank":3,
"Data":"A3"
}
],
"B":[
{
"Rank":1,
"Data":"B1"
},
{
"Rank":2,
"Data":"B2"
},
{
"Rank":3,
"Data":"B3"
}
],
"C":[
{
"Rank":1,
"Data":"C1"
},
{
"Rank":2,
"Data":"C2"
},
{
"Rank":3,
"Data":"C3"
}
]
}
Fiddle
Is there a better way to achieve my goal here?
The same JSON output was achieved using GroupBy first on the Classification and applying ToDictionary on the resulting IGrouping<string, GroupingTestRecord>.Key
var r = records
.GroupBy(_ => _.Classification)
.ToDictionary(
k => k.Key,
v => v.Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank).ToArray()
);
var json = JsonConvert.SerializeObject(r);
Console.WriteLine(json);
which should easily deserialize to the destination single class (for example on a client)
var result = JsonConvert.DeserializeObject<GroupTestResult>(json);
is it possible to get the top level result into a GroupTestResult object?
Build the result from the dictionary
var result = new GroupTestResult {
A = r.ContainsKey("A") ? r["A"] : Enumerable.Empty<GroupTestResultItem>();,
B = r.ContainsKey("B") ? r["B"] : Enumerable.Empty<GroupTestResultItem>();,
C = r.ContainsKey("C") ? r["C"] : Enumerable.Empty<GroupTestResultItem>();,
};
Or this
var result = records.GroupBy(x => x.Classification)
.ToDictionary(x => x.Key, x => x.Select(y => new {y.Rank, y.Data})
.OrderBy(y => y.Rank));
Console.WriteLine(JsonConvert.SerializeObject(result));
Full Demo Here
If I have objects, lets call them Group that has list of some other objects I will call it Brand, and this object has a list of objects called Model.
Is there a way to get only list of Models using MongoDb c# driver.
I tried using SelectMany multiple times but with no success. If I put more than one SelectMany I always get an empty list.
Code should be self-explanatory.
At the end is comment that explains what confuses me.
class Group
{
[BsonId(IdGenerator = typeof(GuidGenerator))]
public Guid ID { get; set; }
public string Name { get; set; }
public List<Brand> Brands { get; set; }
}
class Brand
{
public string Name { get; set; }
public List<Model> Models { get; set; }
}
class Model
{
public string Name { get; set; }
public int Produced { get; set; }
}
class Program
{
static void Main(string[] args)
{
MongoClient client = new MongoClient("mongodb://127.0.0.1:32768");
var db = client.GetDatabase("test");
var collection = db.GetCollection<Group>("groups");
var fca = new Group { Name = "FCA" };
var alfaRomeo = new Brand { Name = "Alfra Romeo" };
var jeep = new Brand { Name = "Jeep" };
var ferrari = new Brand { Name = "Ferrari"};
var laFerrari = new Model { Name = "LaFerrari", Produced = 4 };
var wrangler = new Model { Name = "Wrangler", Produced = 3 };
var compass = new Model { Name = "Compass", Produced = 8 };
var giulietta = new Model { Name = "Giulietta", Produced = 7 };
var giulia = new Model { Name = "Giulia", Produced = 8 };
var _4c = new Model { Name = "4C", Produced = 6 };
fca.Brands = new List<Brand> { ferrari, alfaRomeo, jeep };
ferrari.Models = new List<Model> { laFerrari };
jeep.Models = new List<Model> { wrangler, compass };
alfaRomeo.Models = new List<Model> { giulietta, giulia, _4c };
collection.InsertOne(fca);
Console.WriteLine("press enter to continue");
Console.ReadLine();
var models = collection.AsQueryable().SelectMany(g => g.Brands).SelectMany(b => b.Models).ToList();
Console.WriteLine(models.Count); //returns 0 I expected 6
Console.ReadLine();
}
}
Try
var models = collection.AsQueryable()
.SelectMany(g => g.Brands)
.Select(y => y.Models)
.SelectMany(x=> x);
Console.WriteLine(models.Count());
Working output (with extra Select())
aggregate([{
"$unwind": "$Brands"
}, {
"$project": {
"Brands": "$Brands",
"_id": 0
}
}, {
"$project": {
"Models": "$Brands.Models",
"_id": 0
}
}, {
"$unwind": "$Models"
}, {
"$project": {
"Models": "$Models",
"_id": 0
}
}])
OP Output without extra Select()
aggregate([{
"$unwind": "$Brands"
}, {
"$project": {
"Brands": "$Brands",
"_id": 0
}
}, {
"$unwind": "$Models"
}, {
"$project": {
"Models": "$Models",
"_id": 0
}
}])
How do I need to write to iterate through the results of each of those three initial lists in order to return a single List. Title is the grouping value.
var invoiced = new List<Anonim>
{
new Anonim {Category = 1, Title = "Legal", Amount = 20},
new Anonim {Category = 2, Title = "Accounting", Amount = 10}
}
var settled = new List<Anonim> {
new Anonim {Category = 1, Title = "Legal", Amount = 10}
}
var credit = new List<Anonim> {
new Anonim {Category = 1, Title = "Legal", Amount = 30},
new Anonim {Category = 2, Title = "Accounting", Amount = 20}
}
var result = new List<Result> {
new Result {Title = credit.Title, Invoiced = invoiced.Amount, Settled = settled.Amount, SumAmount = credit.Amount + settled.Amount + invoiced.Amount },
new Result {Title = credit.Title, Invoiced = invoiced.Amount, Settled = settled.Amount, SumAmount = credit.Amount + settled.Amount + invoiced.Amount }
}
public class Result
{
public string Title { get; set; }
public decimal Credit { get; set; }
public decimal Invoiced { get; set; }
public decimal Settled { get; set; }
public decimal SumAmount { get; set; }
}
public class Anonim {
public int Category { get; set; }
public string Title { get; set; }
public decimal Amount { get; set; }
}
SumAmount is the sum of Invoiced, settled, credit of each item
It's a little unclear what you want to happen, but assuming you want to group by the Title property, here's one method. First you project each list into the Result class, making sure to set the relevant properties for each one, union them together into a big list and then group them to get the totals:
var groupedResults = invoiced.Select(i => new Result
{
Title = i.Title,
Invoiced = i.Amount
}).Union(settled.Select(i => new Result
{
Title = i.Title,
Settled = i.Amount
})).Union(credit.Select(i => new Result
{
Title = i.Title,
Credit = i.Amount
}));
var result = groupedResults
.GroupBy(r => r.Title)
.Select(g => new Result
{
Title = g.Key,
Invoiced = g.Sum(r => r.Invoiced),
Settled = g.Sum(r => r.Settled),
Credit = g.Sum(r => r.Credit),
SumAmount = g.Sum(r => r.Invoiced+r.Settled+r.Credit)
});
If I understood you correctly, this is something you want, however this is inefficient due to numerous collection traversals. Maybe #DavidG solution will be more performant.
var groupedSum = invoiced.Union(settled).Union(credit).GroupBy(g => g.Title).Select(g => new Result
{
Title = g.Key,
Credit = credit.Where(c => c.Title == g.Key).Sum(c => c.Amount),
Settled = settled.Where(c => c.Title == g.Key).Sum(c => c.Amount),
Invoiced = invoiced.Where(c => c.Title == g.Key).Sum(c => c.Amount),
SumAmount = g.Sum(i => i.Amount)
});
This is simplest I can find :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication73
{
class Program
{
static void Main(string[] args)
{
List<Anonim> invoiced = new List<Anonim>
{
new Anonim {Category = 1, Title = "Legal", Amount = 20},
new Anonim {Category = 2, Title = "Accounting", Amount = 10}
};
List<Anonim> settled = new List<Anonim> {
new Anonim {Category = 1, Title = "Legal", Amount = 10}
};
List<Anonim> credit = new List<Anonim> {
new Anonim {Category = 1, Title = "Legal", Amount = 30},
new Anonim {Category = 2, Title = "Accounting", Amount = 20}
};
List<Result> results = new List<Result>();
List<string> titles = invoiced.Select(x => x.Title).ToList();
titles.AddRange(settled.Select(x => x.Title).ToList());
titles.AddRange(credit.Select(x => x.Title).ToList();
titles = titles.Distinct().ToList();
foreach(string title in titles)
{
Result newResult = new Result();
results.Add(new Result);
newResult.Title = title;
newResult.Credit = credit.Where(x => x.Title == title).Sum(x => x.Amount);
newResult.Invoiced = invoiced.Where(x => x.Title == title).Sum(x => x.Amount);
newResult.Settled = settled.Where(x => x.Title == title).Sum(x => x.Amount);
newResult.SumAmount = newResult.Credit + newResult.Invoiced + newResult.Settled;
}
}
}
public class Result
{
public string Title { get; set; }
public decimal Credit { get; set; }
public decimal Invoiced { get; set; }
public decimal Settled { get; set; }
public decimal SumAmount { get; set; }
}
public class Anonim
{
public int Category { get; set; }
public string Title { get; set; }
public decimal Amount { get; set; }
}
}
var invoiced = new List<Anonim>
{
new Anonim {Category = 1, Title = "Legal", Amount = 20},
new Anonim {Category = 2, Title = "Accounting", Amount = 10}
};
var settled = new List<Anonim> {
new Anonim {Category = 1, Title = "Legal", Amount = 10}
};
List<Anonim> credit = new List<Anonim> {
new Anonim {Category = 1, Title = "Legal", Amount = 30},
new Anonim {Category = 2, Title = "Accounting", Amount = 20}
};
List<Result> x = new List<Result>();
x.AddRange(invoiced.Select(y => new Result { Title = y.Title, Invoiced = y.Amount }));
x.AddRange(settled.Select(y => new Result { Title = y.Title, Invoiced = y.Amount }));
x.AddRange(credit.Select(y => new Result { Title = y.Title, Invoiced = y.Amount }));
var FinalList = x.GroupBy(a => a.Title).Select(Fn => new Result {
Title =Fn.Key,
Invoiced = Fn.Sum(a => a.Invoiced),``
Settled = Fn.Sum(a => a.Settled),
Credit = Fn.Sum(a => a.Credit),
SumAmount = Fn.Sum(a => a.Invoiced + a.Settled + a.Credit)
});
I have a number of objects and each object has an array, I would like to group these objects by the values inside the array, so conceptually they look as follows:
var objects = new []{
object1 = new object{
elements = []{1,2,3}
},
object2 = new object{
elements = []{1,2}
},
object3 = new object{
elements = []{1,2}
},
object4 = new object{
elements = null
}
}
after grouping:
group1: object1
group2: object2,object3
group3: object4
somethings that I have tried:
actual classes:
public class RuleCms
{
public IList<int> ParkingEntitlementTypeIds { get; set; }
}
var rules = new List<RuleCms>()
{
new RuleCms()
{
ParkingEntitlementTypeIds = new []{1,2}
},
new RuleCms()
{
ParkingEntitlementTypeIds = new []{1,2}
},
new RuleCms()
{
ParkingEntitlementTypeIds = new []{1}
},
new RuleCms()
{
ParkingEntitlementTypeIds = null
}
};
var firstTry = rules.GroupBy(g => new { entitlementIds = g.ParkingEntitlementTypeIds, rules = g })
.Where(x => x.Key.entitlementIds !=null && x.Key.entitlementIds.Equals(x.Key.rules.ParkingEntitlementTypeIds));
var secondTry =
rules.GroupBy(g => new { entitlementIds = g.ParkingEntitlementTypeIds ?? new List<int>(), rules = g })
.GroupBy(x => !x.Key.entitlementIds.Except(x.Key.rules.ParkingEntitlementTypeIds ?? new List<int>()).Any());
You can use IEqualityComparer class. Here is the code:
class MyClass
{
public string Name { get; set; }
public int[] Array { get; set; }
}
class ArrayComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] x, int[] y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(int[] obj)
{
return string.Join(",", obj).GetHashCode();
}
}
Then
var temp = new MyClass[]
{
new MyClass { Name = "object1", Array = new int[] { 1, 2, 3 } },
new MyClass { Name = "object2", Array = new int[] { 1, 2 } },
new MyClass { Name = "object3", Array = new int[] { 1, 2 } },
new MyClass { Name = "object4", Array =null }
};
var result = temp.GroupBy(i => i.Array, new ArrayComparer()).ToList();
//Now you have 3 groups
For simple data that really is as simple as your example you could do this:
.GroupBy(x => string.Join("|", x.IDS))
.Select(x => new
{
IDS = x.Key.Split('|').Where(s => s != string.Empty).ToArray(),
Count = x.Count()
});
i have two generic lists with a few properties to compare but i want that the key identifiers are dynamic by a List<string>.
So lets say we have the class:
class A
{
string Name { get; set; }
string Color1 { get; set; }
string Color2 { get; set; }
string Length { get; set; }
}
The user now can select from an user interface which properties of two lists of those objects need to overlap so that a correct pair is selected. This is stored in a List<string>. As example, if the list string contains "Name" and "Color1" there will be only objects returned where "Name" and "Color1" are overlapping.
I was trying to write a function, but unfortunately i'm not sure which collection i should cast the generic lists to and how do i apply the names of the properties on those? If the name of the "identificators" were always the same, it wouldn't be a problem with Linq/Lambda ;)
Thanks in advance
You need to use reflection for this. This works:
public class A
{
public string Name { get; set; }
public string Color1 { get; set; }
public string Color2 { get; set; }
public string Length { get; set; }
public static IEnumerable<A> Intersecting(IEnumerable<A> input, List<string> propertyNames)
{
if(input == null)
throw new ArgumentNullException("input must not be null ", "input");
if (!input.Any() || propertyNames.Count <= 1)
return input;
var properties = typeof(A).GetProperties();
var validNames = properties.Select(p => p.Name);
if (propertyNames.Except(validNames, StringComparer.InvariantCultureIgnoreCase).Any())
throw new ArgumentException("All properties must be one of these: " + string.Join(",", validNames), "propertyNames");
var props = from prop in properties
join name in validNames.Intersect(propertyNames, StringComparer.InvariantCultureIgnoreCase)
on prop.Name equals name
select prop;
var allIntersecting = input
.Select(a => new {
Object = a,
FirstVal = props.First().GetValue(a, null),
Rest = props.Skip(1).Select(p => p.GetValue(a, null)),
})
.Select(x => new {
x.Object, x.FirstVal, x.Rest,
UniqueValues = new HashSet<object>{ x.FirstVal }
})
.Where(x => x.Rest.All(v => !x.UniqueValues.Add(v)))
.Select(x => x.Object);
return allIntersecting;
}
}
Sample data:
var aList = new List<A> {
new A { Color1 = "Red", Length = "2", Name = "Red" }, new A { Color1 = "Blue", Length = "2", Name = "Blue" },
new A { Color1 = "Red", Length = "2", Name = "A3" }, new A { Color1 = "Blue", Length = "2", Name = "A3" },
new A { Color1 = "Red", Length = "3", Name = "Red" }, new A { Color1 = "Blue", Length = "2", Name = "A6" },
};
var intersecting = A.Intersecting(aList, new List<string> { "Color1", "Name" }).ToList();