I have following class structure
public class PriceClass
{
public int id { get; set; }
public string price { get; set; }
public int product_id { get; set; }
}
public class NameClass
{
public int id { get; set; }
public string name { get; set; }
}
public class ProductDetails
{
public int id { get; set; }
public string product_type { get; set; }
public List<NameClass> nameCl{ get; set; }
public List<PriceClass> priceCl{ get; set; }
}
public class ProductLists
{
public List<ProductDetails> ProductDet{ get; set; }
}
Now I want to sort the ProductLists with price i.e. in priceClass class
I tried with some solution but that does not seems working
prdList = (ProductLists )prdList.ProductDet.OrderBy(r => r.priceCl.OrderBy(x => x.price).ToList());
But it seems i am no where around its solution
Please suggest
Thanks
It is just simpler:
sorted_ProductDet_List = prdList.ProductDet
.OrderBy(prDet => prDet.sortPrice)
.ToList());
Then you'll have to add a Property sortPrice to priceDetail which returns the relevant price used as sort criterium.
The lambda inside OrderBy works on a single element of the original List (List) and specifies the variable/property/etc (probably from any nested class or method) the sorting is based on.
You can use a custom IComparer to compare the product details.
public class ProductDetailsComparer : IComparer<ProductDetails>
{
private bool _compareMinPrice;
public ProductDetailsComparer(bool compareMinPrice)
{
_compareMinPrice = compareMinPrice;
}
public int Compare(ProductDetails x, ProductDetails y)
{
var left = _compareMinPrice ? x.priceCl.Min(p => p.price) : x.priceCl.Max(p => p.price);
var right = _compareMinPrice ? y.priceCl.Min(p => p.price) : y.priceCl.Max(p => p.price);
return left.CompareTo(right);
}
}
You can use the custom comparer in OrderBy as shown below.
// Sort by minimum price
var ascending = productLists.ProductDet.OrderBy(x => x, new ProductDetailsComparer(true)).ToList();
// Sort by maximum price
var descending = productLists.ProductDet.OrderBy(x => x, new ProductDetailsComparer(false)).ToList();
The above is just a sample. You can write a custom comparer suiting your requirements.
Related
How can i fix Cannot implicitly convert System.Collections.Generic.List <RPHistory> to System.Collections.Generic.List <RPHistory> exception error.
I am trying to combine two Entities together to get a single list
RP Entity Class:
public class RP
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
}
RPHistory Entity Class :
public class RPHistory:
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
}
And I created this third class
RpWithHistory Class :
public class RpWithHistory {
public int ID;
public int RPID;
public string Name;
public int ProductID;
public List<RPHistory> History;
}
Linq Query
var RPs = await Context.RP.Where(b => b.ProductID == request.ID)
.Select(x=> new RpWithHistory {
ID = x.ID,
RPID = x.RPID,
Name = x.Name,
ProductID = x.ProductID,
History = Context.RPHistory
.Where(y=> y.RPID
== x.RPID)
.ToList()
}
).ToListAsync();
But i get this error,
>Cannot implicitly convert System.Collections.Generic.List <RPHistory> to
>System.Collections.Generic.List <RPHistory> exception error
Thanks!
I am not sure why you're doing that. Can I suggest this?
You do not need to go all that way of creating a class that joins the two. Just create a Navigation property on your RP that points to RPHistory Objects.
public class RP
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
public ICollection<RPHistory> HistoryList { get; set; } // Navigation Property
}
public class RPHistory:
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
[ForeignKey(nameof(RPID))] // Identify the Foreign Key from RP Class
public RP RP { get; set; } // Navigation back to RP
}
Then you can chain everything into a single list using LINQ:
var RPs = Context.RP.Where(rp => rp.ProductID == request.ID)
.Include(rp=>rp.RPHistory) // This includes RPHistory
.ToList();
You need to clone or create a new list.
Option 1: Use ConvertAll
List<RPHistory> pPHistoryCopy = rphWithHistory.RPHistory.ConvertAll(history => new RPHistory(rphWithHistory.RPHistory));
Option 2:
//Clone Extension
static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}
Use the clone extention
I have two tables "CategoryGroup" and "Category" which returns data in one list. Requirement is to convert flat list to hierarchy list like CategoryGroup contains related categories.
Please suggest how to convert into hieararchy list?
public class ProfileConditionGroup
{
public Guid ConditionGroupGUID { get; set; }
public string ConditionGroupName { get; set; }
public List<ProfileConditionList> profileConditions { get; set; }
}
public class ProfileConditionList
{
public string ConditionName { get; set; }
public Nullable<Guid> ConditionGUID { get; set; }
public string[] ThisConditionSelectedByProfileIDs { get; set; }
public Nullable<Guid> ParentConditionGroupGUID { get; set; }
public bool IsDefaultSelected { get; set; }
}
This is actually a little tricky without having an actual concrete return type, but here's the way I'd tackle it if you're just building a tree of Guid values.
Given this query:
var query =
from pcg in pcgs
from pcl in pcg.profileConditions
select new
{
pcg.ConditionGroupName,
pcg.ConditionGroupGUID,
pcl.ConditionName,
pcl.ConditionGUID,
pcl.ThisConditionSelectedByProfileIDs,
pcl.ParentConditionGroupGUID,
pcl.IsDefaultSelected,
};
...I'd do this:
var lookup = query.ToLookup(x => x.ParentConditionGroupGUID);
Func<Guid?, Tree<Guid?>> build = null;
build = g =>
{
var r = new Tree<Guid?>() { Value = g };
r.AddRange(lookup[g].Select(x => build(x.ConditionGroupGUID)));
return r;
};
Now you just call build(null) and you should get a tree of Guid?. If you can create you're own output class you'd change the function to Func<Guid?, Tree<YourOutputClass>>.
You need this class for the tree:
public class Tree<T> : List<Tree<T>>
{
public T Value { get; set; }
}
I have list of Issues : List<Issue> where Issue is class :
public class Issue : BaseEntity
{
private string m_KeyString;
[JsonProperty("expand")]
public string Expand { get; set; }
[JsonProperty("id")]
public int Id { get; set; }
#region Special key solution
[JsonProperty("key")]
public string ProxyKey
{
get
{
return Key.ToString();
}
set
{
m_KeyString = value;
}
}
[JsonIgnore]
public IssueKey Key
{
get
{
return IssueKey.Parse(m_KeyString);
}
}
#endregion Special key solution
[JsonProperty("fields")]
public Fields Fields { get; set; }
}
Class Fields looks like this :
public class Fields
{
[JsonProperty("summary")]
public string Summary { get; set; }
[JsonProperty("assignee")]
public Assignee Assignee { get; set; }
[JsonProperty("worklog")]
public List<WorkLog> WorkLogs { get; set; }
}
And class WorkLog:
public class WorkLog : BaseEntity
{
[JsonProperty("updateAuthor")]
public string Author { get; set; }
[JsonProperty("timeSpent")]
public string TimeSpent { get; set; }
[JsonProperty("timeSpentSeconds")]
public int TimeSpentSeconds { get; set; }
}
I want to get on output Author - SUM (TimeSpentSeconds)
So i need to group by Author and then get SUM.
For each item in Issues list I make this :
var sumTime = issue.Fields.WorkLogs.GroupBy(x => x.Author).Select(x => new
{
User = x.Key.Name,
Amount = x.Sum(s => s.TimeSpentSeconds)
});
which group by user and count sum.
But how can i manage the same not just for one item in list but for all list?
OK, I think you're saying that you can run this query for a single Issue, but you want to be able to run it over all the items in a List<Issue>.
I'm assuming that you don't want to break it out separately by Issue, so what I'd do is this:
List<Issue> issues = // get Issues from wherever
var sumTime = issues.SelectMany(issue => issue.Fields.WorkLogs)
.GroupBy(x => x.Author)
.Select( x => new
{
x.Key.Name,
Amount = x.Sum(s => s.TimeSpentSeconds)
}
);
Disclaimer:
this was done entirely from memory, with no testing.
I want to return the item that has the profile ID I send. So in order to do this I will need to loop through all of the Items -> WebProproperties -> profile. The Class structure is at the end of the question.
I would rather use LINQ than create a nested foreach. I have been trying to get this to work for more than an hour now. I am stuck.
My first idea was to simply use where. But that doesn't work because you need to have something on the other side that needs to equal.
this.Accounts.items.Where(a => a.webProperties.Where(b => b.profiles.Where(c => c.id == pSearchString)) ).FirstOrDefault();
My second idea was to try using Exists which I don't have much experience with:
Item test = from item in this.Accounts.items.Exists(a => a.webProperties.Exists(b => b.profiles.Exists(c => c.id == pSearchString))) select item;
This doesn't work either:
Could not find an implementation of query pattern for source type 'Bool'
public RootObject Accounts {get; set;}
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
public List<Item> items { get; set; }
}
public class Profile
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
public class WebProperty
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string internalWebPropertyId { get; set; }
public string level { get; set; }
public string websiteUrl { get; set; }
public List<Profile> profiles { get; set; }
}
public class Item
{
public string id { get; set; }
public string kind { get; set; }
public string name { get; set; }
public List<WebProperty> webProperties { get; set; }
}
You can use Any() to determine existence. Also, note that many of the extension methods have overloads which take a predicate, including FirstOrDefault():
this.Accounts.items.FirstOrDefault(a => a.webProperties
.Any(b => b.profiles
.Any(c => c.id == pSearchString)));
You are looking for the .Any() operation I think. This will return true/false for whether there are any items matching your query.
For example:
if (this.Accounts.Items.Any(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)));
EDIT: You have full answer (was posted while I was composing mine) and as pointed out in comments my answer isn't actually returning your found item, just letting you know whether there is one. You can rework the first .Any to be a .FirstOrDefault to get that match.
E.g.
var result = this.Accounts.Items.FirstOrDefault(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)))
You can use the below mentioned code.
var abc = rr.items.Where(p => p.webProperties.Any(c => c.profiles.Any(d => d.id == "1"))).FirstOrDefault();
Just for your reference, your class should look like:
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
private List<Item> _items=new List<Item>();
public List<Item> items
{
get { return _items; }
set { _items = value; }
}
}
I have the following class:
public class PingtreeTier
{
public BuyerType BuyerType { get; set; }
public int ID { get; set; }
public int MaxRequests { get; set; }
public IEnumerable<PingtreeNode> Nodes { get; set; }
public int Seq { get; set; }
public int Timeout { get; set; }
public bool Weighted { get; set; }
}
As you can see PingtreeTier contains an IEnumerable<PingtreeNode> class. This PingtreeNode class has a property named Status. Using Linq, I need to select only the Tiers/Nodes where PingtreeNode Status = 'Active'.
Anyone help as I'm struggling with the syntax for this.
How about using .Any or .All here:
var results = tiers.Where(t => t.Nodes.Any(n => n.Status == "Active"));
This will select any PingtreeTiers that contain at least one PingTreeNode with Status equal to "Active".
If you wanted to select only PingtreeTiers whose PingTreeNodes are all active, you could use the .All extension method instead:
var results = tiers.Where(t => t.Nodes.All(n => n.Status == "Active"));