Select section of list based on value of a enumeration - c#

I have a c# app. I have custom list of type result, shown below. The list is called 'myResultList'.
enumResult { noResult = 0, win = 1, lose = 2 }
class Result
{
public enumResult OutCome {get; set;}
public double Frequency {get;set;}
public string GroupName {get; set;}
public double TotalValue {get; set;}
}
myResultList contains numerous elements. I wish to select all the elements where the Outcome equals lose into a new list. I believe LINQ is probably best for this task, correct me if I am wrong. How do I go about querying a list based on a enumeration?

var lostResults = myResultList.Where(r => r.OutCome == enumResult.lose).ToList();
NOTE: Consider to have Pascal Case names for types and public members. And don't include prefixes in type names. E.g.
public enum Outcome
{
NoResult,
Win,
Lose
}
If you will need to filter results by other types of outcomes, then consider to use lookup:
var results = myResultList.ToLookup(r => r.OutCome);
Then getting results by their type will be easy:
var wonResults = results[enumResult.won];

var newList = myResultList.Where(r => r.OutCome == enumResult.lose).ToList();

Related

Update model sort order [duplicate]

Any idea why the LINQ OrderBy is not working in following code, (have no errors but method does not sort ...)
First my own type
public class IQLinksView
{
public int id { get; set; }
public int catid { get; set; }
public int? viewed {get;set;}
public string name {get;set;}
public string desc {get;set;}
public string url {get;set;}
public string pic {get;set;}
public string cat {get;set;}
}
then query :
IQueryable<IQLinksView> newView =
from links in this.emContext.tbl_otherlinks
select new IQLinksView { id = links.pklinkid, catid =
links.tbl_catgeory.pkcategoryid, viewed = links.linkviewed, name = links.linkname,
desc = links.linkdesc, pic = links.linkpicture, url = links.linkurl, cat =
links.tbl_catgeory.categoryname };
Untill here all fine :-), but then
newView.OrderBy(x => x.viewed);
just changes nothing,... Page is loading results showing ... but no ordering ... sniff
i have Try with (creating a comparer object ... ):
newView.OrderBy(x => (Int32)x.viewed, new CompareIntegers());
same result, no ordering ...
I do have workarounds but just wondering what is missing ....
Any suggestions will be appreciated thanks a lot :-)
Don't throw away the return value. The OrderBy extension method is does not mutate the input. Try:
newView = newView.OrderBy(x => x.viewed);
There is no reason why that won't work, assuming the viewed value is correct. Also, make sure that OrderBy is after any operations (e.g. Distinct) which will ruin ordering.
Happy coding!
No-Tracking Queries
Consider use the asnotracking() after orderby() if the result is a readonly result.
Example:
query = query.OrderByDescending(x => x.Rating).AsNoTracking();

Sort properties given attribute paramter order

I want to sort properties based off an attribute parameter Order that is given.
Attribute:
public class Items: Attribute
{
public int Order { get; set; }
public Items(int order)
{
this.Order = order;
}
}
Implementation:
public class client
{
[Items(1)]
public string fName {get; set;}
[Items(3)]
public string lName {get; set;}
[Items(2)]
public string mName {get; set;}
}
Get all Properties:
var properties = typeof(client).GetProperties().Where(
prop => prop.IsDefined(typeof(Items), false));
Sort by Order#?
This is what I tried but it does not work
Array.Sort(properties, delegate (Items x, Items y)
{ return x.Order.CompareTo(y.Order); });
How do I sort the properties based off the Order?
This has been solved, but would like to extend the question.
Is there a way to sort properties without having to put an "Order" on them. I am wanting to just have an attribute "EndOfList" Or "Last" that would state be sure to sort these last. So that I would not have to clutter up the code with Orders.
You can use Linq and OrderBy
var sorted = properties
.OrderBy(p => ((Items)p.GetCustomAttributes(true)
.FirstOrDefault(a => a is Items)).Order);
This results in following order: fName, mName, lName.
So what happens inside the OrderBy: access custom properties. Find a first that is of type Items and use the Order property as a sort parameter.
To sort in reversed order just use OrderByDescending.
You can try this
var properties = typeof(client).GetProperties().Where(prop => prop.IsDefined(typeof(Items), false));
var sortedProperties = properties.OrderBy(x => ((Items)x.GetCustomAttributes(typeof(Items), false).FirstOrDefault()).Order);

C# new variable or reference?

public class Price
{
public string Symbol {get; set; }
public double AskPrice{get; set; }
public double BidPrice{get; set; }
public string Exchange{get; set; }
}
public class inputs
{
public IList<Price> Prices {get; set; }
}
var inputs = new
{
Prices = prices,
};
Price[] p = inputs.Prices.Where(x => x.Exchange == exchange).ToArray();
p.ForEach(x => x.AskPrice = 0);
For this code when I create new variable p, it is actually a reference to input.price, not a new variable. Why is this? Is there any best practice of how to deal with this behavior?
You did not make a change to p, p stayed the same, what you changed where the elements inside of p, the elements inside of p are shared between p and the original source.
To not get this behavior you need to "Deep copy" the objects when you make a new array, creating new objects for the elements with the same content as the original.
public class Price
{
public string Symbol {get; set; }
public double AskPrice{get; set; }
public double BidPrice{get; set; }
public string Exchange{get; set; }
public Price Clone()
{
var result = new Price();
result.Symbol = this.Symbol;
result.AskPrice = this.AskPrice;
result.BidPrice = this.BidPrice;
result.Exchange = this.Exchange;
return result;
}
}
public class inputs
{
public IList<Price> Prices {get; set; }
}
var inputs = new
{
Prices = prices,
};
Price[] p = inputs.Prices.Where(x => x.Exchange == exchange).Select(x=> x.Clone()).ToArray();
p.ForEach(x => x.AskPrice = 0);
Note, if you have any reference types inside of your class you need to recursively clone the entire data structure and will need to make copies of them too.
There are two different variables here - the first is the Price object(s), and the second is input.Prices, which is a List of prices.
Your LINQ code takes the inputs.Prices list, filters it and creates a new array from it, but all that does is create new collections. It doesn't change the actual objects that are in the collection. This is because classes, in C#, are all reference types, meaning that var price = input.Prices[0] just copies the reference to a single, specific instance in memory. You can copy those references between a dozen lists and arrays, but the objects are the same.
It seems that what you want is to clone or copy by value your Price objects. For that, you have two options:
Make Price a struct.
Structs, unlike classes, are value types and are copied-by-value, meaning a new copy is made whenever you assign it to a new variable. This, however, has a performance penalty, since the whole struct is copied every time it's assigned. Your struct takes up 24-32 bytes (two 64bit doubles and two 32/64 bit references to strings), which is more than the recommended rule of thumb of "no more than 16 bytes for structs", so it's probably a bad idea.
Make a Clone method.
Have your Price implement a Clone method which returns a copy of the object - or alternately, create a copy-constructor that creates a new Price with the old values. Use that in your LINQ:
public class Price
{
// your fields
public Price Clone()
{
return new Price
{
Symbol = this.Symbol,
BidPrice = this.BidPrice,
//etc.
}
}
}
var p = input.Prices.Where(x => x.Exchange == exchange).Select(x => x.Clone()).ToArray();

Linq update issue with lambda

I am trying to write some code in Linq with lambda.This is my first code using lambda and i am facing an issue while updating Record.
My code is:
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
Table<NOTIF_RECIP> NOTIF_RECIP_alias = db.GetTable<NOTIF_RECIP>();
Table<NOTIF_SCHED> NOTIF_SCHED_alias = db.GetTable<NOTIF_SCHED>();
Table<mainframe_replication> mainframe_replication_alias = db.GetTable<mainframe_replication>();
var ids = NOTIF_SCHED_alias.Select(x => x.NOTIF_RPT_ID).ToArray();
foreach (string notif_sched_data in ids)
{
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias,
mfr => mfr.RPT_ID,
nr => nr.NOTIF_RECIP_ID,
(mfr, nr) => new
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= mfr.NOTIF_ID,
RecipAdd=nr.NOTIF_RECIP_ADDR
});
foreach(var repljoinmf_data in repljoinmf)
{
//DO STUFF
repljoinmf_data.NotifId = "Changedxyz";
//db.SubmitChanges();
}
}
}
I am getting Error in repljoinmf_data.NotifId = "Changedxyz";
Error says: Error 2 Property or indexer 'AnonymousType#3.NotifId' cannot be assigned to -- it is read only
Can someone please help me in this.I think it is because I am using var which is anonymous but how to solve the problem.Any help is appreciated.
Thanks
As the error suggests, anonymous class instances cannot be modified once they have been projected.
Although you could switch to a strong typed class, and then reassign the member properties, however, you have an opportunity to project the desired result in the preceding LINQ statement into the same anonymous class:
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias, mfr => mfr.RPT_ID, nr => nr.NOTIF_RECIP_ID,
(mfr, nr) => new // Anon Class projection
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= "Changedxyz", // *** No need to mutate this afterwards
RecipAdd=nr.NOTIF_RECIP_ADDR
});
Edit, Update isn't trivial assignment, suggested alternatives
Option #1 : Strongly typed Class with mutation after projection
Add a new class (I've guessed some types)
public class MyPoco
{
public int ReportId {get; set;}
public string Reportversion {get; set;}
public byte[] ReportBytes {get; set;}
public DateTime ReportDate {get; set;}
public int NotifId {get; set;}
public string RecipAdd {get; set;}
}
Which you can then project into (just specify the class name instead of anonymous):
(mfr, nr) => new MyPoco // Not anonymous
{
ReportId=mfr.RPT_ID,
...
And then do modification afterwards:
foreach(var repljoinmf_data in repljoinmf)
{
repljoinmf_data.NotifId = "SomeNewValue"
Option #2 - Create a method (or Func) which does the complex logic
Since you seem to have already materialized all the data, you are free to use complex functions in the property projections. Any of the available local variables (closure) are available to pass to thus function, as are the join lambda parameters (mfr, nr)
So for example, write a function to calculate your NotifId = "Changedxyz" replacement:
private string DoIntensiveLogic(mainframe_replication mfr, NOTIF_RECIP nr)
{
// Do Stuff
}
Which you can then use in your original anonymous projection:
(mfr, nr) => new // Anon Class projection
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= DoIntensiveLogic(mfr, nr), // Call the function each row
RecipAdd=nr.NOTIF_RECIP_ADDR
});
Anonymous types are immutable and hence created cannot be changed you have to create a new type.
To solve your issue you have to create your own type and avoid the use of anonymous type when a future update is needed.
your type may look like this
public class ReportInfo
{
public int Id{get; set;}
//the same thing for others properties
}
and your query will look like this
new ReportInfo() {
Id = mfr.RPT_ID,
Reportversion = mfr.RPT_VERS,
ReportBytes = mfr.RPT_BYTES.ToString(),
ReportDate = mfr.REPL_DTM.ToString(),
NotifId = mfr.NOTIF_ID,
RecipAdd = nr.NOTIF_RECIP_ADDR
})
than you can update easily your property
foreach(var repljoinmf_data in repljoinmf)
{
//DO STUFF
repljoinmf_data.NotifId = "Changedxyz";
//db.SubmitChanges();
}
More about anonymous Types
what the compiler is actually doing. When you write a line of code like this:
var o = new { property1 = expression1, ..., propertyN = expressionN };
the compiler infers the type of each expression, creates private fields of these inferred types, creates
public read-only properties for each of the fields, and creates a constructor that accepts all these
expressions. The constructor’s code initializes the private read-only fields from the expression results
passed in to it. In addition, the compiler overrides Object’s Equals, GetHashCode, and ToString
methods and generates code inside all these methods.
if you want to change 'NotifId' later, you can find a record by id and change the property.
Example:
var alias = mainframe_replication_alias.SingleOrDefault(mfr => mfr.NOTIF_ID == repljoinmf_data.NotifId);
if(alias != null)
alias.NOTIF_ID = "Changedxyz";

Sort ArrayList with custom comparison

I am trying to sort an ArrayList using c#. When the ArrayList contains comparable objects, it is possible to sort with using list.Sort() but I need to sort an ArrayList which contains non-comparable objects. For example, let's say the object is Ring and it has an attribute property Price. Then I need to sort the ArrayList to the price order. If is is possible to select ascending or descending that will more helpful. Thank You!
Blockquote
arrAtdMon = **(ArrayList)**hashTb[unixMon];
if (arrAtdMon != null)
monCount = arrAtdMon.Count;
int[] arrayMax = { monCount, tueCount, wedCount, thuCount, friCount };
int maxValue = arrayMax.Max();
KidAttendance valMon = null;
string monTagName = string.Empty;
Blockquote
above array list is to be sorted it self.
You can do this by implementing IComparer interface:-
public class Ring : IComparer
{
public decimal Price { get; set; }
public int Compare(object x, object y)
{
return ((Ring)x).Price.CompareTo(((Ring)y).Price);
}
}
Working Fiddle.
First, you really should be using the List<T> class, not ArrayList. Doing so wouldn't solve your problem, but it would make the code less fragile and more easy to maintain.
As for the specific question, you want to do something like this…
Assume:
class Ring { public decimal Price { get; set; } }
Then:
ArrayList list = ...; // Initialized as some collection of Ring instances
list.Sort(Comparer.Create((r1, r2) => r1.Price.CompareTo(r2.Price)));
This creates a new Comparer instance using the Comparison<T> of (r1, r2) => r1.Price.CompareTo(r2.Price). That is, for each pair of objects being compared, compare the price of the first with the price of the second.
Assuming that these objects share a base class or an interface with the price property you should be able to do something like this:
// Base class with price property, could also be an shared interface
public abstract class Product
{
public decimal Price{get;set;}
}
public class Ring : Product
{
}
public class Bag : Product
{
}
// Some test data
var myUnsortedArray = new Product[]{new Ring{Price = 1.2m}, new Bag{Price=2.5m}};
// Easy sort with LINQ
var sortedProducts = myUnsortedArray.OrderBy(p => p.Price).ToArray();
var sortedProductsDescending = myUnsortedArray.OrderByDescending(p => p.Price).ToArray();
UPDATE
I just realised that the question is about ArrayLists and have the changed solution below:
// Some test data
var myUnsortedArrayList = new ArrayList{new Ring{Price = 1.2m}, new Bag{Price=2.5m}};
// Easy sort with LINQ
var sortedProducts = myUnsortedArrayList.OfType<Product>().OrderBy(p => p.Price).ToArray();
var sortedProductsDescending = myUnsortedArrayList.OfType<Product>().OrderByDescending(p => p.Price).ToArray();
To sort an set of objects, the object needs to be Comparable and you can set up the comparison you'd like in the CompareTo() method:
IComparable information here

Categories