Related
The "AddToLeg" method seems long winded is there a better pattern or more efficient way to implement this? I thought about using a dictionary but I want the key to remain an integer. I'm pretty new to linq / generics so I maybe missing something more obvious. When I've looked through documentation there aren't really any examples that match my scenario.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
public class Program
{
public static void Main()
{
var model = new TrainspotterItenaryViewModel();
var manchester = new Station() { Name = "Manchester", ExpectedTime = "13:30" };
var leeds = new Station() { Name = "Leeds", ExpectedTime = "15:00" };
var york = new Station() { Name = "York", ExpectedTime = "15:30" };
var london = new Station() { Name = "London", ExpectedTime = "21:00" };
model.AddToLeg(1, manchester);
model.AddToLeg(1, leeds);
model.AddToLeg(2, leeds);
model.AddToLeg(2, london);
model.AddToLeg(1, york); //another destination added to leg 1
//any number of legs can be added...
model.AddToLeg(3, manchester);
//show results contents
for(var i=1; i <= model.Legs.Count; i++)
{
var displayLeg = model.Legs.Single(x=>x.LegNumber==i);
foreach(var station in displayLeg.Stations){
string output = $"leg: {displayLeg.LegNumber} station: {station.Name}, expected:{station.ExpectedTime}";
Console.WriteLine(output);
}
}
}
}
public class TrainspotterItenaryViewModel
{
public List<Leg> Legs { get; set; }
public void AddToLeg(int legNumber, Station station)
{
if (Legs == null)
{
Legs = new List<Leg>();
}
var legCount = Legs.Count(x => x.LegNumber == legNumber);
if (legCount == 0)
{
var leg = new Leg
{
LegNumber = legNumber,
Stations = new List<Station> {station}
};
Legs.Add(leg);
Console.WriteLine($"Leg {leg.LegNumber} Not Found- Added new leg and {station.Name}");
}
else
{
foreach (var leg in Legs)
{
if (leg.LegNumber == legNumber)
{
leg.Stations.Add(station);
Console.WriteLine($"Leg {legNumber} Found- adding {station.Name}");
}
}
}
}
}
public class Leg
{
public int LegNumber { get; set; }
public List<Station> Stations { get; set; }
}
public class Station
{
public string Name { get; set; }
public string ExpectedTime { get; set; }
}
}
Use Dictionary for this task:
public class TrainspotterItenaryViewModel
{
private Dictionary<int, Leg> _legNumberToLegIndex { get; set; }
public IEnumerable<Leg> Legs => _legNumberToLegIndex?.Values
public void AddToLeg(int legNumber, Station station)
{
if (_legNumberToLegIndex == null)
{
_legNumberToLegIndex = new Dictionary<int, Leg>();
}
Leg leg;
if (!_legNumberToLegIndex.TryGetValue(legNumber, out leg))
{
leg = new Leg
{
LegNumber = legNumber,
Stations = new List<Station>()
};
_legNumberToLegIndex.Add(legNumber, leg);
}
leg.Stations.Add(station);
}
}
The method FirstOrDefault already returns the object that you're searching and so you can simplify your code
public List<Leg> Legs { get; set; } = new List<Leg>();
public void AddToLeg(int legNumber, Station station)
{
var leg = Legs.FirstOrDefault(x => x.LegNumber == legNumber);
if (leg == null)
{
leg = new Leg
{
LegNumber = legNumber,
Stations = new List<Station> { station }
};
Legs.Add(leg);
Console.WriteLine($"Leg {leg.LegNumber} Not Found- Added new leg and {station.Name}");
}
else
{
leg.Stations.Add(station);
Console.WriteLine($"Leg {legNumber} Found- adding {station.Name}");
}
}
I have the following classes hierarchy:
abstract class Product : IComparable
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Barcode { get; set; }
public int CompareTo(object obj)
{
int ret = -1;
if (String.Compare(this.GetType().Name, obj.GetType().Name,StringComparison.Ordinal) == 0)
ret = 0;
return ret;
}
}
abstract class Book : Product
{
public int PagesCount { get; set; }
}
class ProgrammingBook : Book
{
public string ProgrammingLanguage { get; set; }
}
class CulinaryBook : Book
{
public string MainIngridient { get; set; }
}
class EsotericBook : Book
{
public int MininumAge { get; set; }
}
abstract class Disc : Product
{
internal enum Content
{
Music,
Video,
Software
}
public Content DiscContent { get; set; }
}
class CdDisc : Disc
{
}
class DvdDisc : Disc
{
}
And I trying to sort following collection by using IComparable interface method CompareTo:
List<Product> products = new List<Product>
{
new DvdDisc {Name = "The lord of the rings 2",DiscContent = Disc.Content.Video,Price = 200M,Barcode = "5435443-2"},
new CdDisc {Name = "Antonio Vivaldi: best picks",Price = 700M, DiscContent = Disc.Content.Music,Barcode = "4543765-565"},
new CulinaryBook{Name = "Midterranian foods",MainIngridient = "Salmon",PagesCount = 436,Price = 350M,Barcode = "41457561-897"},
new CdDisc{Name = "Windows XP", DiscContent = Disc.Content.Software, Price = 950M, Barcode = "5433668-4"},
new EsotericBook{Name = "Russian Freemasonry 1731-2000",MininumAge = 21,PagesCount = 2100,Price = 3000M,Barcode = "6464632-876"},
new CdDisc {Name = "The best of Mussorgsky",Price = 300M, DiscContent = Disc.Content.Music,Barcode = "5435436-567"},
new ProgrammingBook{Name = "CLR via C#",PagesCount = 900, Price = 1110M,ProgrammingLanguage = "C#",Barcode = "5546533-2446"},
new DvdDisc {Name = "The lord of the rings 1",DiscContent = Disc.Content.Video,Price = 200M,Barcode = "54354423-2"},
new ProgrammingBook{Name = "ASP.NET MVC 4",PagesCount = 800,Price = 1200M,ProgrammingLanguage = "C#",Barcode = "46476573-65"},
new EsotericBook{Name = "Russian Freemasonry in it's future and past",MininumAge =19, PagesCount = 900, Price = 2342M,Barcode = "3656353-24"},
new CulinaryBook{Name = "Traditional Polish dishes",MainIngridient = "Red meat",PagesCount = 630,Price = 840,Barcode = "54634234-5"}
}; products.Sort();
Output list should look like this:
1.1 Programming books
1.2 Culinary books
1.3 Esoteric books
2.1 Cd discs sorted by content
2.2 DVD discs sorted by content
My current CompareTo method does only part of the job - comparing classes by name.
This is a working example that will render the exact same output:
1.1 Programming books
1.2 Culinary books
1.3 Esoteric books
2.1 Cd discs sorted by content
2.2 DVD discs sorted by content
I also added IComparable<Product> to more easily make it comparable with other products.
abstract class Product : IComparable<Product>
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Barcode { get; set; }
protected abstract int InternalSortOrder { get; }
protected virtual string SortBy { get {return Name;} }
public int CompareTo(Product obj)
{
var sameType = string.Compare(GetType().Name, obj.GetType().Name, StringComparison.Ordinal) == 0;
var sameBaseType = GetType().BaseType != null && obj.GetType().BaseType != null &&
string.Compare(GetType().BaseType.ToString(), obj.GetType().BaseType.ToString(),
StringComparison.Ordinal) == 0;
// They have the same base type, but not the same type. Order by base type first.
if (!sameType && !sameBaseType && GetType().BaseType != null && obj.GetType().BaseType != null)
{
// Order by base type first.
return string.Compare(GetType().BaseType.ToString(), obj.GetType().BaseType.ToString(),
StringComparison.Ordinal);
}
// it's the same base type (eg. book or disc)
if (sameBaseType)
{
// Order by sort order.
if (obj.InternalSortOrder != this.InternalSortOrder)
{
return InternalSortOrder.CompareTo(obj.InternalSortOrder);
}
}
if (sameType)
{
// Same sort order. We sort by name.
return string.Compare(SortBy, obj.SortBy, StringComparison.Ordinal);
}
// Order by Type.
return string.Compare(GetType().Name, obj.GetType().Name, StringComparison.Ordinal);
}
}
abstract class Book : Product
{
public int PagesCount { get; set; }
}
class ProgrammingBook : Book
{
public string ProgrammingLanguage { get; set; }
protected override int InternalSortOrder
{
get { return 1; }
}
}
class CulinaryBook : Book
{
public string MainIngridient { get; set; }
protected override int InternalSortOrder
{
get { return 2; }
}
}
class EsotericBook : Book
{
public int MininumAge { get; set; }
protected override int InternalSortOrder
{
get { return 3; }
}
}
abstract class Disc : Product
{
internal enum Content
{
Music,
Video,
Software
}
protected override string SortBy
{
get { return DiscContent.ToString(); }
}
public Content DiscContent { get; set; }
}
class CdDisc : Disc
{
protected override int InternalSortOrder
{
get { return 1; }
}
}
class DvdDisc : Disc
{
protected override int InternalSortOrder
{
get { return 2; }
}
}
UPDATE:
I've added virtual SortByin Product. It will go to Name by default, but Disc will return Content.
You need to work with equals option
rtn = Primary Sort;
if(rtn =0)
rtn =secondary Sort;
if(rtn =0)
rtn =tertiary Sort;
as your primary sort seems to be on the type of book
you would do
rtn = this.GetType().Name.CompareTo(obj.GetType().Name);
if(rtn ==0)
rtn =this.Name.CompareTo(obj.Name);
return rtn;
if your problem is that you don't want alphabetic sorting on the type
then add to product
public abstract int SortOrder{get;}
and use that as your primary sort
I have a requirement to reflect on a object get all properties that are collections and
1)GetCount for each collection
2)GetTotalCount (allCollectionCount)
3)Call a method with this collection.
Below is what I have done so far with a made up noddy structure for semplicity.
I am stuck in how to call this method and how to get count for collection.
Any suggestions?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
var request = GetDataRequest();
//Get all properties
List<PropertyInfo> propInfoList =
new List<PropertyInfo>(request.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public));
//Get collections only
var myClassCollections=propInfoList.Where(xxx => xxx.PropertyType.GetInterfaces().Any(x => x == typeof (IEnumerable))).ToList();
var totalCountForAllCollections=????
foreach (var col in myClassCollections)
{
//How do I call my Method DoSomething
// DoSomething<?>(col.?????)
}
}
public void DoSomething<T>(List<T> objectCollection)
{
//etc...
}
private static DataRequest GetDataRequest()
{
DataRequest request = new DataRequest();
request.Addresses.Add(new Address
{
Id = 1,
City = "London",
Postcode = "32131",
Street = "London Road"
});
request.Addresses.Add(new Address
{
Id = 2,
City = "NewYork",
Postcode = "3432",
Street = "NewYork Road"
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jo",
Surname = "Bloggs",
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jon",
Surname = "Bloggs2",
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jonny",
Surname = "Bloggs3",
});
return request;
}
}
public class DataRequest
{
public DataRequest()
{
Customers = new List<Customer>();
Orders = new List<Order>();
Addresses = new List<Address>();
}
public List<Customer> Customers { get; set; }
public List<Order> Orders { get; set; }
public List<Address> Addresses { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public string OrderNo { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
}
quick and dirty, here you go...
// ..
static class Program
{
static void Main()
{
var request = GetDataRequest();
//Get propertyValues for properties that are enumerable (i.e. lists,arrays etc)
var collectionProperties = request.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(propertInfo => propertInfo.PropertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)))
.Select(p => p.GetValue(request, null))
.Cast<IEnumerable<object>>().ToList();
var totalCountForAllCollections = 0;
// iterate through the list of propertyValues
foreach (var collectionPropertyValue in collectionProperties)
{
totalCountForAllCollections += collectionPropertyValue.Count();
collectionPropertyValue.DoSomething();
}
System.Console.WriteLine("The total count for all collections is : {0}", totalCountForAllCollections);
System.Console.WriteLine("press any key to exit");
System.Console.ReadLine();
}
public static void DoSomething<T>(this IEnumerable<T> objectCollection)
{
//etc...
// N.B. you will have to use typeof(T) to implement logic specific to the type
// If the logic in this method is non-specific to the typeof(T) then Implement logic accordingly
System.Console.WriteLine("The type of the collection is: {0}", objectCollection.GetType());
System.Console.WriteLine("The count of items in this collection is:{0}", objectCollection.Count());
}
// ..
}
// ..
I have below scenario:
This is my class structure :
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public System.Collections.ObjectModel.Collection<Likes> Likes { get; set; }
}
public class Likes
{
public string Sport { get; set; }
public string Music { get; set; }
public string Food { get; set; }
public string Place { get; set; }
}
When I serialize object of User class then it will generate the below json string :
{"FirstName":"Naresh",
"LastName":"Parmar",
"Likes": [{"Sport":"Cricket",
"Music":"Classic",
"Food":"Gujarati",
"Place":"India"}]
}
I want to generate above json string like below:
{"FirstName":"Naresh",
"LastName":"Parmar",
"Sport":"Cricket",
"Music":"Classic",
"Food":"Gujarati",
"Place":"India"
}
I want the nested properties as primary one.
Any help would be appreciated.
Thanks in advance..
EDIT:
{"FirstName":"Naresh",
"LastName":"Parmar",
"Sport":"Cricket,Chess,Football",
"Music":"Classic",
"Food":"Gujarati",
"Place":"India"
}
It's really bad practice, since the code i'll post bellow doesn't have great maintainability, however if that's what you looking for, you can use this. Another class that have the format that you'd like, and have a method that adds a list of likes to the format you've required. That the class you should serialize to JSON:
class NestedUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Sport { get; set; }
public string Music { get; set; }
public string Food { get; set; }
public string Place { get; set; }
public void AddLikes(System.Collections.ObjectModel.Collection<Likes> likes)
{
foreach (Likes like in likes)
{
Sport += like.Sport + ",";
Music += like.Music + ",";
Food += like.Food + ",";
Place += like.Place + ",";
}
if (Sport != string.Empty)
{
Sport = Sport.Substring(0, Sport.Length - 1);
}
if (Music != string.Empty)
{
Music = Music.Substring(0, Music.Length - 1);
}
if (Food != string.Empty)
{
Food = Food.Substring(0, Food.Length - 1);
}
if (Place != string.Empty)
{
Place = Place.Substring(0, Place.Length - 1);
}
}
}
Since it's not only limited to Likes objects I'd suggest using dynamic objects. So the User class I propose is as follows:
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public dynamic Details { get; set; }
public User()
{
Details = new ExpandoObject();
}
public void AddSingleDetail(string key, string value)
{
var dict = this.Details as IDictionary<string, Object>;
if (dict.ContainsKey(key))
{
dict[key] += "," + value;
}
else
{
dict[key] = value;
}
}
public void AddDetails(object detailsObject)
{
var type = detailsObject.GetType();
foreach (var prop in type.GetProperties())
{
AddSingleDetail(prop.Name, prop.GetValue(detailsObject).ToString());
}
}
}
You can use it for adding single proerpties or adding an object as a whole. I used reflection to get all the property name and values and add them to the user details.
Sample usage:
static void Main(string[] args)
{
var user1 = new User() { FirstName = "Homer", LastName = "Simpson" };
user1.AddSingleDetail("Sport", "Bowling");
user1.AddSingleDetail("Sport", "Sleeping");
user1.AddSingleDetail("Food", "Donut");
user1.AddSingleDetail("Music", "Rock");
string flattenedHomer1 = ConvertUserToFlattenedJson(user1);
var user2 = new User() { FirstName = "Homer", LastName = "Simpson" };
var likes1 = new Likes() { Food = "Donut", Music = "Rock", Place = "Springfield", Sport = "Bowling" };
var likes2 = new Likes() { Food = "Steaks", Music = "Metal", Place = "Evergreen Terrace", Sport = "Sleeping" };
var proStuff = new ProfessionalStuff() { Title = "Boss" };
user2.AddDetails(likes1);
user2.AddDetails(likes2);
user2.AddDetails(proStuff);
string flattenedHomer2 = ConvertUserToFlattenedJson(user2);
}
And the method performing the JSON conversion is:
public static string ConvertUserToFlattenedJson(User u)
{
dynamic flatUser = new ExpandoObject();
flatUser.FirstName = u.FirstName;
flatUser.LastName = u.LastName;
var dict = u.Details as IDictionary<string, Object>;
foreach (var like in dict)
{
((IDictionary<string, Object>)flatUser)[like.Key] = like.Value;
}
string json = Newtonsoft.Json.JsonConvert.SerializeObject(flatUser);
return json;
}
In my sample above user2 is converted to the following JSON string which I believe is what you are looking for:
{
"FirstName": "Homer",
"LastName": "Simpson",
"Sport": "Bowling,Sleeping",
"Music": "Rock,Metal",
"Food": "Donut,Steaks",
"Place": "Springfield,Evergreen Terrace",
"Title": "Boss"
}
While concatenating strings you can check for null or duplicate values. I didn't handle that part.
For the sake of completeness, here's the ProfessionalStuff class I made up:
public class ProfessionalStuff
{
public string Title { get; set; }
}
Hope this helps.
I need to get data as per this json format.
series: [{
name: 'Marriage',
data: [1, 2, 3] // Sample Data
}, {
name: 'Chess',
data: [2, 2, 3]
}, {
name: 'Ludo',
data: [3, 4, 4]
}]
I need to create chart as here in http://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/bar-stacked/
What I have tried is using group by from device id and and using for loop to get the result. But I am quite stuck here getting required output.
Here is what I have tried so far.
void Main()
{
DateTime currentDate = DateTime.UtcNow.Date.AddDays(-30);
var currentMonthData = Device_GameDevices.Where(x => x.CreatedDate >= currentDate).ToList();
// Get total game list
var gamesList = currentMonthData.Select(x => x.GameName).Distinct();
// Group the data as per the device id
var groupedData = from gameData in currentMonthData
group gameData by gameData.DeviceID
into egroup
select new {
Game = egroup.Key,
Data = from bug in egroup
group bug by bug.GameName into g2
select new { Name = g2.Key, HoursPlayed = g2.Sum(x => (x.EndTime - x.StartTime).TotalMinutes/60) }
};
Console.Write(groupedData);
List<DashboardVM.ChartData> chartDatas = new List<DashboardVM.ChartData>();
List<double> hourResultList = new List<double>();
foreach(var item in groupedData)
{
var chart = new DashboardVM.ChartData();
foreach(var gameItem in gamesList)
{
chart.GameNameResult = gameItem;
foreach(var groupedDataItem in item.Data)
{
if(gameItem == groupedDataItem.Name)
{
hourResultList.Add(groupedDataItem.HoursPlayed);
}
else
{
hourResultList.Add(0.0);
}
}
chart.HoursPlayed = hourResultList;
}
chartDatas.Add(chart);
}
Console.Write(chartDatas);
}
public class DashboardVM{
public class ChartData{
public string GameNameResult{get;set;}
public List<double> HoursPlayed{get;set;}
}
}
public class Chart
{
public string type { get; set; }
}
public class Title
{
public string text { get; set; }
}
public class XAxis
{
public List<string> categories { get; set; }
}
public class Title2
{
public string text { get; set; }
}
public class YAxis
{
public int min { get; set; }
public Title2 title { get; set; }
}
public class Legend
{
public bool reversed { get; set; }
}
public class Series
{
public string stacking { get; set; }
}
public class PlotOptions
{
public Series series { get; set; }
}
public class Series2
{
public string name { get; set; }
public List<double> data { get; set; }
}
public class RootObject
{
public Chart chart { get; set; }
public Title title { get; set; }
public XAxis xAxis { get; set; }
public YAxis yAxis { get; set; }
public Legend legend { get; set; }
public PlotOptions plotOptions { get; set; }
public List<Series2> series { get; set; }
}
void Main()
{
var Device_GameDevices = new[] {
new {ID=1,CreatedDate=DateTime.Parse("8/23/2017 06:07:30"),DeviceID="Desktop12",EndTime=DateTime.Parse("8/23/2017 06:06:30"),GameName="CyberGunner",StartTime=DateTime.Parse("8/23/2017 06:03:45")},
new {ID=2,CreatedDate=DateTime.Parse("8/23/2017 07:14:01"),DeviceID="A12" ,EndTime=DateTime.Parse("8/23/2017 11:14:01"),GameName="Marriage" ,StartTime=DateTime.Parse("8/23/2017 07:14:01")},
new {ID=3,CreatedDate=DateTime.Parse("8/23/2017 07:14:02"),DeviceID="A12" ,EndTime=DateTime.Parse("8/23/2017 08:14:01"),GameName="Marriage" ,StartTime=DateTime.Parse("8/23/2017 07:14:02")},
new {ID=4,CreatedDate=DateTime.Parse("8/23/2017 09:14:01"),DeviceID="A12" ,EndTime=DateTime.Parse("8/23/2017 09:14:01"),GameName="Chess" ,StartTime=DateTime.Parse("8/23/2017 07:14:03")},
new {ID=5,CreatedDate=DateTime.Parse("8/23/2017 07:14:03"),DeviceID="A12" ,EndTime=DateTime.Parse("8/23/2017 10:14:01"),GameName="Marriage" ,StartTime=DateTime.Parse("8/23/2017 07:14:03")},
new {ID=6,CreatedDate=DateTime.Parse("8/23/2017 09:57:28"),DeviceID="B12" ,EndTime=DateTime.Parse("8/23/2017 10:57:28"),GameName="Marriage" ,StartTime=DateTime.Parse("8/23/2017 09:57:28")},
};
DateTime currentDate=DateTime.UtcNow.Date.AddDays(-30);
var currentMonthData=Device_GameDevices
.Where(x=>x.CreatedDate>=currentDate)
.ToList();
// Get total game list
var gamesList=currentMonthData
.Select(x=>x.GameName)
.Distinct()
.ToList();
var chart=new RootObject
{
chart=new Chart{ type="bar"},
title=new Title{ text="My title" },
xAxis=new XAxis { categories=gamesList },
yAxis=new YAxis { min=0, title=new Title2 {text="Total Game Time"}},
legend=new Legend {reversed=true},
plotOptions=new PlotOptions { series=new Series {stacking="normal"}},
series=currentMonthData
.GroupBy(d=>new {d.DeviceID,d.GameName})
.Select(d=>new {
DeviceID=d.Key.DeviceID,
GameName=d.Key.GameName,
HoursPlayed=d.Sum(x=>(x.EndTime - x.StartTime).TotalMinutes)/60
})
.GroupBy(d=>d.DeviceID)
.Select(d=>new Series2 {
name=d.Key,
data=gamesList
.GroupJoin(d,a=>a,b=>b.GameName,(a,b)=>new {GameName=a,HoursPlayed=b.Sum(z=>z.HoursPlayed)})
.OrderBy(x=>gamesList.IndexOf(x.GameName))
.Select(x=>x.HoursPlayed)
.ToList()
}).ToList()
};
chart.Dump();
}
This is how the series looks: