I have a LINQ to object query to select all the persons that are above 20 years old
IEnumerable<Object> result = null;
result = (from person in AllPersons.ToList()
where person.age > 20
select new
{
FirstName= person.FirstName,
LastName= person.LastName,
Email= person.Email,
PhoneNumber= person.PhoneNumber
});
return result;
I have a parameter string SortProperty I want to use to sort the result based on the property.
So for example if SortProperty="FirstName" I want to sort the result based on the first name.
I tried to do the following:
return result.OrderBy(x => x.GetType().GetProperty(SortProperty));
but it did not work
any idea how to do it?
PS: I don't want to test all the possibilities, and do a if-else on each, or a case switch. I'm looking for an efficient way to do this
Thanks
Check out the Dynamic Linq Extensions Libraries...
It has extension Methods which accept strings instead of Properties.
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Since your SortProperty is already a string you could do
var result = (from person in AllPersons.ToList()
where person.age > 20
select new
{
FirstName= person.FirstName,
LastName= person.LastName,
Email= person.Email,
PhoneNumber= person.PhoneNumber
}
).OrderBy(SortProperty);
return result;
Also, depending on what AllPersons is, it might not make sense to Enumerate that by calling ToList() until the end. e.g.
var result = (from person in AllPersons
...
).OrderBy(SortProperty).ToList();
Try
return result.OrderBy(x => x.GetType().GetProperty(SortProperty).GetValue(x, null));
return result.OrderBy( x => TypeHelper.GetPropertyValue( x, sortProperty ) )
.ToList();
I'm using something like this :
var sortExpression = #"A,C";
var expressions = sortExpression.Split(new[] { ',' });
var cmpPredicates = new Dictionary<string, Func<Person, Person, int>>(3);
cmpPredicates.Add(#"A", (x, y) => x.A.CompareTo(y.A));
cmpPredicates.Add(#"B", (x, y) => x.B.CompareTo(y.B));
cmpPredicates.Add(#"C", (x, y) => x.C.CompareTo(y.C));
cmpPredicates.Add(#"Default", (x, y) => x.Id.CompareTo(y.Id));
var currentPredicates = new Func<Person, Person, int>[expressions.Length + 1];
for (int i = 0; i < expressions.Length; i++)
{
currentPredicates[i] = cmpPredicates[expressions[i]];
}
// Default sort order
currentPredicates[currentPredicates.Length - 1] = cmpPredicates[#"Default"];
persons.Sort((x, y) =>
{
var cmp = 0;
var index = 0;
while (cmp == 0 && index < currentPredicates.Length)
{
cmp = currentPredicates[index++](x, y);
}
return cmp;
});
where the Person class has the following definition
public class Person
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public long Id { get; set; }
public Person()
{
this.A = string.Empty;
this.B = string.Empty;
this.C = string.Empty;
}
}
The main benefit is the multiproperty support. With additional checks(duplicates & exist & predicate limit) is can be user provided.
Related
I have a class called Order which has properties such as OrderId, OrderDate, Quantity, and Total. I have a list of this Order class:
List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders
I want to sort the list based on one property of the Order object; for example, either by the order date or the order id.
How can I do this in C#?
The easiest way I can think of is to use Linq:
List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
If you need to sort the list in-place then you can use the Sort method, passing a Comparison<T> delegate:
objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
If you prefer to create a new, sorted sequence rather than sort in-place then you can use LINQ's OrderBy method, as mentioned in the other answers.
To do this without LINQ on .Net2.0:
List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
delegate(Order p1, Order p2)
{
return p1.OrderDate.CompareTo(p2.OrderDate);
}
);
If you're on .Net3.0, then LukeH's answer is what you're after.
To sort on multiple properties, you can still do it within a delegate. For example:
orderList.Sort(
delegate(Order p1, Order p2)
{
int compareDate = p1.Date.CompareTo(p2.Date);
if (compareDate == 0)
{
return p2.OrderID.CompareTo(p1.OrderID);
}
return compareDate;
}
);
This would give you ascending dates with descending orderIds.
However, I wouldn't recommend sticking delegates as it will mean lots of places without code re-use. You should implement an IComparer and just pass that through to your Sort method. See here.
public class MyOrderingClass : IComparer<Order>
{
public int Compare(Order x, Order y)
{
int compareDate = x.Date.CompareTo(y.Date);
if (compareDate == 0)
{
return x.OrderID.CompareTo(y.OrderID);
}
return compareDate;
}
}
And then to use this IComparer class, just instantiate it and pass it to your Sort method:
IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);
Simplest way to order a list is to use OrderBy
List<Order> objListOrder =
source.OrderBy(order => order.OrderDate).ToList();
If you want to order by multiple columns like following SQL Query.
ORDER BY OrderDate, OrderId
To achieve this you can use ThenBy like following.
List<Order> objListOrder =
source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
Doing it without Linq as you said:
public class Order : IComparable
{
public DateTime OrderDate { get; set; }
public int OrderId { get; set; }
public int CompareTo(object obj)
{
Order orderToCompare = obj as Order;
if (orderToCompare.OrderDate < OrderDate || orderToCompare.OrderId < OrderId)
{
return 1;
}
if (orderToCompare.OrderDate > OrderDate || orderToCompare.OrderId > OrderId)
{
return -1;
}
// The orders are equivalent.
return 0;
}
}
Then just call .sort() on your list of Orders
A Classical Object Oriented Solution
First I must genuflect to the awesomeness of LINQ.... Now that we've got that out of the way
A variation on JimmyHoffa answer. With generics the CompareTo parameter becomes type safe.
public class Order : IComparable<Order> {
public int CompareTo( Order that ) {
if ( that == null ) return 1;
if ( this.OrderDate > that.OrderDate) return 1;
if ( this.OrderDate < that.OrderDate) return -1;
return 0;
}
}
// in the client code
// assume myOrders is a populated List<Order>
myOrders.Sort();
This default sortability is re-usable of course. That is each client does not have to redundantly re-write the sorting logic. Swapping the "1" and "-1" (or the logic operators, your choice) reverses the sort order.
// Totally generic sorting for use with a gridview
public List<T> Sort_List<T>(string sortDirection, string sortExpression, List<T> data)
{
List<T> data_sorted = new List<T>();
if (sortDirection == "Ascending")
{
data_sorted = (from n in data
orderby GetDynamicSortProperty(n, sortExpression) ascending
select n).ToList();
}
else if (sortDirection == "Descending")
{
data_sorted = (from n in data
orderby GetDynamicSortProperty(n, sortExpression) descending
select n).ToList();
}
return data_sorted;
}
public object GetDynamicSortProperty(object item, string propName)
{
//Use reflection to get order type
return item.GetType().GetProperty(propName).GetValue(item, null);
}
Using LINQ
objListOrder = GetOrderList()
.OrderBy(o => o.OrderDate)
.ToList();
objListOrder = GetOrderList()
.OrderBy(o => o.OrderId)
.ToList();
Here is a generic LINQ extension method that does not create an extra copy of the list:
public static void Sort<T,U>(this List<T> list, Func<T, U> expression)
where U : IComparable<U>
{
list.Sort((x, y) => expression.Invoke(x).CompareTo(expression.Invoke(y)));
}
To use it:
myList.Sort(x=> x.myProperty);
I recently built this additional one which accepts an ICompare<U>, so that you can customize the comparison. This came in handy when I needed to do a Natural string sort:
public static void Sort<T, U>(this List<T> list, Func<T, U> expression, IComparer<U> comparer)
where U : IComparable<U>
{
list.Sort((x, y) => comparer.Compare(expression.Invoke(x), expression.Invoke(y)));
}
Please let me complete the answer by #LukeH with some sample code, as I have tested it I believe it may be useful for some:
public class Order
{
public string OrderId { get; set; }
public DateTime OrderDate { get; set; }
public int Quantity { get; set; }
public int Total { get; set; }
public Order(string orderId, DateTime orderDate, int quantity, int total)
{
OrderId = orderId;
OrderDate = orderDate;
Quantity = quantity;
Total = total;
}
}
public void SampleDataAndTest()
{
List<Order> objListOrder = new List<Order>();
objListOrder.Add(new Order("tu me paulo ", Convert.ToDateTime("01/06/2016"), 1, 44));
objListOrder.Add(new Order("ante laudabas", Convert.ToDateTime("02/05/2016"), 2, 55));
objListOrder.Add(new Order("ad ordinem ", Convert.ToDateTime("03/04/2016"), 5, 66));
objListOrder.Add(new Order("collocationem ", Convert.ToDateTime("04/03/2016"), 9, 77));
objListOrder.Add(new Order("que rerum ac ", Convert.ToDateTime("05/02/2016"), 10, 65));
objListOrder.Add(new Order("locorum ; cuius", Convert.ToDateTime("06/01/2016"), 1, 343));
Console.WriteLine("Sort the list by date ascending:");
objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
foreach (Order o in objListOrder)
Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);
Console.WriteLine("Sort the list by date descending:");
objListOrder.Sort((x, y) => y.OrderDate.CompareTo(x.OrderDate));
foreach (Order o in objListOrder)
Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);
Console.WriteLine("Sort the list by OrderId ascending:");
objListOrder.Sort((x, y) => x.OrderId.CompareTo(y.OrderId));
foreach (Order o in objListOrder)
Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);
//etc ...
}
Anybody working with nullable types, Value is required to use CompareTo.
objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));
//Get data from database, then sort list by staff name:
List<StaffMember> staffList = staffHandler.GetStaffMembers();
var sortedList = from staffmember in staffList
orderby staffmember.Name ascending
select staffmember;
An improved of Roger's version.
The problem with GetDynamicSortProperty is that only get the property names but what happen if in the GridView we use NavigationProperties? it will send an exception, since it finds null.
Example:
"Employee.Company.Name; " will crash... since allows only "Name" as a parameter to get its value.
Here's an improved version that allows us to sort by Navigation Properties.
public object GetDynamicSortProperty(object item, string propName)
{
try
{
string[] prop = propName.Split('.');
//Use reflection to get order type
int i = 0;
while (i < prop.Count())
{
item = item.GetType().GetProperty(prop[i]).GetValue(item, null);
i++;
}
return item;
}
catch (Exception ex)
{
throw ex;
}
}
You can do something more generic about the properties selection yet be specific about the type you're selecting from, in your case 'Order':
write your function as a generic one:
public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector)
{
return (from order in orders
orderby propertySelector(order)
select order).ToList();
}
and then use it like this:
var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate);
You can be even more generic and define an open type for what you want to order:
public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector)
{
return (from item in collection
orderby propertySelector(item)
select item).ToList();
}
and use it the same way:
var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate);
Which is a stupid unnecessary complex way of doing a LINQ style 'OrderBy',
But it may give you a clue of how it can be implemented in a generic way
var obj = db.Items.Where...
var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
Suppose you have the following code, in this code, we have a Passenger class with a couple of properties that we want to sort based on.
public class Passenger
{
public string Name { get; }
public string LastName { get; }
public string PassportNo { get; }
public string Nationality { get; }
public Passenger(string name, string lastName, string passportNo, string nationality)
{
this.Name = name;
this.LastName = lastName;
this.PassportNo = passportNo;
this.Nationality = nationality;
}
public static int CompareByName(Passenger passenger1, Passenger passenger2)
{
return String.Compare(passenger1.Name, passenger2.Name);
}
public static int CompareByLastName(Passenger passenger1, Passenger passenger2)
{
return String.Compare(passenger1.LastName, passenger2.LastName);
}
public static int CompareNationality(Passenger passenger1, Passenger passenger2)
{
return String.Compare(passenger1.Nationality, passenger2.Nationality);
}
}
public class TestPassengerSort
{
Passenger p1 = new Passenger("Johon", "Floid", "A123456789", "USA");
Passenger p2 = new Passenger("Jo", "Sina", "A987463215", "UAE");
Passenger p3 = new Passenger("Ped", "Zoola", "A987855215", "Italy");
public void SortThem()
{
Passenger[] passengers = new Passenger[] { p1, p2, p3 };
List<Passenger> passengerList = new List<Passenger> { p1, p2, p3 };
Array.Sort(passengers, Passenger.CompareByName);
Array.Sort(passengers, Passenger.CompareByLastName);
Array.Sort(passengers, Passenger.CompareNationality);
passengerList.Sort(Passenger.CompareByName);
passengerList.Sort(Passenger.CompareByLastName);
passengerList.Sort(Passenger.CompareNationality);
}
}
So you can implement your sort structure by using Composition delegate.
I made this extension method for List<T>.
The extension method takes the property you wish to sort as a parsed string and then uses the OrderBy method of the List<T>. Then it sets each index of the original list to the same index of the ordered list.
public static class ListExtensions {
public static void SortBy<T>(this List<T> list, string property, bool reverse = false) {
List<T> ordered = list.OrderBy(obj => obj.GetType().GetProperty(property).GetValue(obj, null)).ToList();
for (int i = 0; i < list.Count; i++)
list[i] = reverse ? ordered[list.Count - 1 - i] : ordered[i];
}
}
If an object in the list has the property Name you sort your list testList as so:
//For normal sorting order
testList.SortBy("Name");
//For reverse sorting order
testList.SortBy("Name", true);
I would recommend that you change the name of SortBy, to something like Prefix_SortBy. To prevent potential collisions if you import another library.
I know this method works for alphabetical and numerical ordering. Its sorting capabilites may be limited yet it is very simple to operate.
If there are some major flaws or issues with this, do tell, I've been programming C# for about 3 months.
Best regards
Make use of LiNQ OrderBy
List<Order> objListOrder=new List<Order> ();
objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
Based on GenericTypeTea's Comparer :
we can obtain more flexibility by adding sorting flags :
public class MyOrderingClass : IComparer<Order> {
public int Compare(Order x, Order y) {
int compareDate = x.Date.CompareTo(y.Date);
if (compareDate == 0) {
int compareOrderId = x.OrderID.CompareTo(y.OrderID);
if (OrderIdDescending) {
compareOrderId = -compareOrderId;
}
return compareOrderId;
}
if (DateDescending) {
compareDate = -compareDate;
}
return compareDate;
}
public bool DateDescending { get; set; }
public bool OrderIdDescending { get; set; }
}
In this scenario, you must instantiate it as MyOrderingClass explicitly( rather then IComparer )
in order to set its sorting properties :
MyOrderingClass comparer = new MyOrderingClass();
comparer.DateDescending = ...;
comparer.OrderIdDescending = ...;
orderList.Sort(comparer);
None of the above answers were generic enough for me so I made this one:
var someUserInputStringValue = "propertyNameOfObject i.e. 'Quantity' or 'Date'";
var SortedData = DataToBeSorted
.OrderBy(m => m.GetType()
.GetProperties()
.First(n =>
n.Name == someUserInputStringValue)
.GetValue(m, null))
.ToList();
Careful on massive data sets though. It's easy code but could get you in trouble if the collection is huge and the object type of the collection has a large number of fields.
Run time is NxM where:
N = # of Elements in collection
M = # of Properties within Object
If you need to sort the Id that is string in Question entity
Use Sort function and delegate to sort the Id after parsing the Id
value
class Question
{
public List<QuestionInfo> Questions Info{ get; set; }
}
class QuestionInfo
{
public string Id{ get; set; }
public string Questions{ get; set; }
}
var questionnaire = new Question();
questionnaire.QuestionInfo.Sort((x, y) => int.Parse(x.Id, CultureInfo.CurrentCulture) - int.Parse(y.Id, CultureInfo.CurrentCulture));
From performance point of view the best is to use a sorted list so that data is sorted as it is added to result.
Other approaches need at least one extra iteration on data and most create a copy of data so not only performance but memory usage will be affected too. Might not be an issue with couple of hundreds of elements but will be with thousands, especially in services where many concurrent requests may do sorting at the same time.
Have a look at System.Collections.Generic namespace and choose a class with sorting instead of List.
And avoid generic implementations using reflection when possible, this can cause performance issues too.
hi just to come back at the question.
If you want to sort the List of this sequence "1" "10" "100" "200" "2" "20" "3" "30" "300" and get the sorted items in this form 1;2;3;10;20;30;100;200;300 you can use this:
public class OrderingAscending : IComparer<String>
{
public int Compare(String x, String y)
{
Int32.TryParse(x, out var xtmp);
Int32.TryParse(y, out var ytmp);
int comparedItem = xtmp.CompareTo(ytmp);
return comparedItem;
}
}
and you can use it in code behind in this form:
IComparer<String> comparerHandle = new OrderingAscending();
yourList.Sort(comparerHandle);
Firstly I have seen IEqualityComparer for anonymous type and the answers there do not answer my question, for the obvious reason that I need an IEqualityComparer not and IComparer for use with Linq's Distinct() method. I have checked the other answers too and these fall short of a solution...
The Problem
I have some code to manipulate and pull records in from a DataTable
var glext = m_dtGLExt.AsEnumerable();
var cflist =
(from c in glext
orderby c.Field<string>(m_strpcCCType),
c.Field<string>(m_strpcCC),
c.Field<string>(m_strpcCCDesc),
c.Field<string>(m_strpcCostItem)
select new
{
CCType = c.Field<string>(m_strpcCCType),
CC = c.Field<string>(m_strpcCC),
CCDesc = c.Field<string>(m_strpcCCDesc),
CostItem = c.Field<string>(m_strpcCostItem)
}).Distinct();
but I need the distinct method to be case insensitive. What is throwing me here is the use of anonymous types.
Attempted Solution 1
If I had SomeClass which had concrete objects I could obviously do
public class SumObject
{
public string CCType { get; set; }
public string CC { get; set; }
public string CCDesc { get; set; }
public string CostItem { get; set; }
}
I could obviously do this
List<SumObject> lso = new List<SumObject>()
{
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Rooney", CostItem = "I477" },
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Zidane", CostItem = "I677" },
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Falcao", CostItem = "I470" },
};
var e = lso.Distinct(new SumObjectComparer()); // Great :]
where
class SumObjectComparer : IEqualityComparer<SumObject>
{
public bool Equals(SumObject x, SumObject y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.CCType.CompareNoCase(y.CCType) == 0 &&
x.CC.CompareNoCase(y.CC) == 0 &&
x.CCDesc.CompareNoCase(y.CCDesc) == 0 &&
x.CostItem.CompareNoCase(y.CostItem) == 0;
}
public int GetHashCode(SumObject o)
{
if (Object.ReferenceEquals(o, null))
return 0;
int hashCCType = String.IsNullOrEmpty(o.CCType) ?
0 : o.CCType.ToLower().GetHashCode();
int hashCC = String.IsNullOrEmpty(o.CC) ?
0 : o.CC.ToLower().GetHashCode();
int hashCCDesc = String.IsNullOrEmpty(o.CCDesc) ?
0 : o.CCDesc.ToLower().GetHashCode();
int hashCostItem = String.IsNullOrEmpty(o.CostItem) ?
0 : o.CostItem.ToLower().GetHashCode();
return hashCCType ^ hashCC ^ hashCCDesc ^ hashCostItem;
}
}
However, the use of anonymous types in the above Linq query are throwing me.
Attempted Solution 2
To attempt another solution to this (and because I have the same issue elsewhere) I generated the following generic comparer class
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
Func<T, T, bool> compareFunction;
Func<T, int> hashFunction;
public GenericEqualityComparer(Func<T, T, bool> compareFunction, Func<T, int> hashFunction)
{
this.compareFunction = compareFunction;
this.hashFunction = hashFunction;
}
public bool Equals(T x, T y) { return compareFunction(x, y); }
public int GetHashCode(T obj) { return hashFunction(obj); }
}
so that I could attempt to do
var comparer = new GenericEqualityComparer<dynamic>(
(x, y) => { /* My equality stuff */ },
o => { /* My hash stuff */ });
but this casts the returned value as IEnumerable<dynamic> which in turn effects my forthcoming use of cflist, so that in a following query the join fails.
var cf =
(from o in cflist
join od in glext
on new { o.CCType, o.CC, o.CCDesc, o.CostItem } equals new
{
CCType = od.Field<string>(m_strpcCCType),
CC = od.Field<string>(m_strpcCC),
CCDesc = od.Field<string>(m_strpcCCDesc),
CostItem = od.Field<string>(m_strpcCostItem)
}
into c
select new { ... }
I don't want to get into ugly casting to and from IEnumerable<T>s due to the heavy use of this code...
Question
Is there a way I can create my an IEquailityComparer for my anonymous types?
Thanks for your time.
Is there a way I can create my an IEquailityComparer for my anonymous types?
Sure. You just need to use type inference. For example, you could have something like:
public static class InferredEqualityComparer
{
public static IEqualityComparer<T> Create<T>(
IEnumerable<T> example,
Func<T, T, bool> equalityCheck,
Func<T, int> hashCodeProvider)
{
return new EqualityComparerImpl<T>(equalityCheck, hashCodeProvider);
}
private sealed class EqualityComparerImpl<T> : IEqualityComparer<T>
{
// Implement in the obvious way, remembering the delegates and
// calling them appropriately.
}
}
Then:
var glext = m_dtGLExt.AsEnumerable();
var query = from c in glext
orderby ...
select new { ... };
var comparer = InferredEqualityComparer.Create(query,
(x, y) => { ... },
o => { ... }
);
var distinct = query.Distinct(comparer);
Basically the first parameter to the method is just used for type inference, so that the compiler can work out what type to use for the lambda expression parameters.
You could create the comparer ahead of time by creating a sample of the anonymous type:
var sample = new[] { new { ... } };
var comparer = InferredExqualityComparer.Create(sample, ...);
var distinct = (... query here ... ).Distinct(comparer);
but then any time you change the query you've got to change the sample too.
This post may get what you want. Although for .NET 2.0 it also works for newer versions (see the bottom of this post for how to achieve this). In contrast to Jon Skeets solution we won´t use a factory-method like create. But this is only syntactic sugar I think.
I saw a similar question here with a very good solutions:
Simplest way to form a union of two lists
But the problem here is, it works when there is only one parameter in each list (int value). I had this rquirement to combine 5 different lists containing objects of the same class without data redundancy and the final list should be sorted out in ascending order of int value.
Example:
Class Company //data Class
{
int companyNo;
string Name;
}
Class CompanyList : List<Company>
{
.................
public CompanyList GetList(int userID)
{
.....
}
}
Class company has a pulic method returning list of companies corresponding to a search criteria, let us userID.
CompanyList list1 = CompanyList .GetList(userID1);
CompanyList list2 = CompanyList .GetList(userID2);
CompanyList list3 = CompanyList .GetList(userID3);
CompanyList list4 = CompanyList .GetList(userID4);
CompanyList list5 = CompanyList .GetList(userID5);
The solution I implemented is (worked well):
CompanyList _finalList = list1;
*foreach (CompanyList _list in {_list2 ,_list3 ,_list4 ,_list5}) //loop thorugh all other list
{
foreach (Company item in _list)
{
for (int i = 0; i <= _finalList.Count - 1; i++)
{
if (_finalList.Item(i).CompanyNo== item.CompanyNo)
//***EXIT TAKE NEXT LIST - GO TO *
}
if (i == _finalList.Count - 1) //else check end of first list
{
//company no. not yet encountered(new)
int pos = 0;
foreach (Company companyInfo in _finalList) //search for position for new company no.
{
if (companyInfo.CompanyNo> item.CompanyNo)
{
break;
}
else
{
pos = pos + 1; //increment position
}
}
_finalList.Insert(pos, item); 'Add new item
}
}
}
**the code is converted from VB.Net to C#. Here I could not find the quivalent code piece for this line so replaced it with the concept.
I am not an expert C# programmer and just wondering if there is any better or simpler way to do this?
Data example:
Input:
list1[0] = {0,"TCS"};
list1[1] = {1,"Infosys"};
list2[0] = {8,"IBM"};
list3[1] = {1,"Infosys"};
list4[0] = {0,"TCS"};
list5[0] = {9,"Accenture"};
list5[1] = {6,"HCL"};
Output:
finalList[0] = {0,"TCS"};
finalList[1] = {1,"Infosys"};
finalList[2] = {6,"HCL"};
finalList[3] = {8,"IBM"};
finalList[4] = {9,"Accenture"};
Regards
Sj
Okay, so you have a number of sequences of something, in your case "something" would be Company, which doesn't overide object.Equals or object.HashCode.
So, a new extension like this, might prove useful
public static IEnumerable<T> Union(
this IEnumerable<T> source,
IEqualityComparer<T> comparer,
params IEnumerable<T>[] others)
{
if (comparer == null)
{
comparer = EqualityComparer<T>.Default;
}
var result = source.Distinct(comparer);
foreach(var o in source)
{
if (o == null)
{
continue;
}
result = result.Union(o, comparer);
}
return result;
}
To make this, and other functions that take an IEqualityComparer simple to use, you could add this class to your code,
public class EqualityComparerImproved<T> : EqaulityComparer<T>
{
private readonly Func<T, T> equalityComparison;
private readonly Func<T, int> hashGenerator;
private EqualityComparerImproved(
Func<T, T> equalityComparison,
Func<T, int> hashGenerator)
{
this.equalityComparison = equalityComparison;
this.hashGenerator = hashGenerator;
}
public static EqualityComparerImproved<T> Create
Func<T, T> equalityComparison,
Func<T, int> hashGenerator)
{
if (equalityComparison == null)
{
throw new ArgumentNullException("equalityComparison");
}
if (hashGenerator == null)
{
throw new ArgumentNullException("hashGenerator");
}
return new EqualityComparerImproved<T>(
equalityComparison,
hashGenerator);
}
public override bool Equals(T x, T y)
{
return this.equalityComparison(x, y);
}
public override int GetHashCode(T obj)
{
return this.hashGenerator(obj);
}
}
Once these two, admittedly lengthy, bits of code were in place you could do
var output = list1.Union(
EqualityComparerImproved<Company>.Create(
(x, y) => x.companyNo == y.companyNo && x.Name == y.Name,
(obj) =>
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = hash * 23 + obj.companyNo;
hash = hash * 23 + obj.Name.GetHashCode();
return hash;
}
},
list2,
list3,
list4,
list5);
or if companyNo is a unique key,
var output = list1.Union(
EqualityComparerImproved<Company>.Create(
(x, y) => x.companyNo == y.companyNo,
(obj) => obj.companyNo),
list2,
list3,
list4,
list5);
would suffice.
Similar to Habib's solution, but a bit more concise and complete.
int[] userIDs = new[] { userID1, userID2, userID3, userID4, userID5 };
IEnumerable<Company> distinctCompanies =
from companyList in userIDs.Select(CompanyList.GetList)
from company in companyList
group company by company.companyNo into companiesWithSameNo
select companiesWithSameNo.First();
CompanyList finalList = new CompanyList();
finalList.AddRange(distinctCompanies);
You might have a constructor in CompanyList that directly accepts an IEnumerable<Company>, too, so you could directly pass distinctCompanies there instead.
You can use either GroupBy or Union to remove duplicates... Union makes for a little cleaner linq (I think) but either can work... the downside is that you also need a custom IEqualityComparer in this case since equals on your company objects will return false (since they are different instances)... An alternative is to have your Company class implement IEqualityComparer and just copy the code I have implementing that interface into your Company class.
// Union gives you a unique list if it knows how to compare the objects properly
var companyEqualityComparer = new CompanyEqualityComparer();
foreach (var companyList in new List<List<Company>>(){list2, list3, list4, list5})
{
combinedList = combinedList.Union(companyList, companyEqualityComparer);
}
// Order your output list
var finalList = combinedList.OrderBy(c => c.companyNo).ToList();
Define your CompanyEqualityComparer...
// CompanyEqualityComparer which is needed since your companies are different instances
public class CompanyEqualityComparer : IEqualityComparer<Company>
{
public bool Equals(Company x, Company y)
{
return x.companyNo.Equals(y.companyNo);
}
public int GetHashCode(Company obj)
{
return obj.companyNo.GetHashCode();
}
}
I think you need something like:
List<Company> inputList = //Get your input List
List<Company> outputList = inputList.GroupBy(r => r.companyNo)
.Select(grp => new Company
{
companyNo = grp.Key,
Name = grp.First().Name,
})
.OrderBy(r=> r.companyNo)
.ToList();
public class Derp
{
public Derp
{
listOfStrings = new List<string>();
}
public string strName;
public List<string> listOfStrings;
public int unrequiredInt;
public bool unrequiredBool;
}
List<Derp> derp1 = ... //generate data assume strName is unique in list, but not across lists;
List<Derp> derp2 = ... //generate data;
List<Derp> derp3 = ... //generate data;
List<Derp> mergedDerp = new List<Derp>();
I need to merge derp1 and derp2 and derp3 with the condition derp1[x].strName == derp2[y].strName == derp3[z].strName. The merged list should have all Derps but merge derp1,2,3 into one derp based on the condition above (unrequiredInt and unrequiredBool's content doesn't matter). I know it can be done in LINQ but I'm quite at a loss. Something like ...
mergedDerp = derp1.Join(derp2, d1 => derp1, d2 => derp2, (d1,d2) => new { ... ;
//and the next derp would be (i assume)
mergedDerp = mergedDerp.Join(derp3, md => mergedDerp, ...;
But i'm not getting it.
The result should contain a list of unique Derps by their strName, and if any Derps were merged, the listOfStrings should all be appended into the new Derp.
Using GroupBy instead of Join seems more suitable in your case:
var mergedDerp = derp1.Union(derp2).Union(derp3).GroupBy(x => x.strName)
.Select(x => new Derp
{
strName = x.Key,
// I guess you want to merge the list of strings as well?
listOfStrings = x.SelectMany(d => d.listOfStrings).ToList()
// Leave unrequired fields as default or just use the first derp's value
// unrequiredInt = x.First().unrequiredInt,
// unrequiredBool = x.First().unrequiredBool,
})
.ToList();
It sounds like you want to determine equality based on the strName value. If so, simply implement the Equals and GetHashCode methods on the object:
public class Derp
{
public Derp()
{
listOfStrings = new List<string>();
}
public string strName;
public List<string> listOfStrings;
public int unrequiredInt;
public bool unrequiredBool;
public override bool Equals(object obj)
{
return ((Derp) obj).strName.Equals(strName);
}
public override int GetHashCode()
{
return strName.GetHashCode();
}
}
Then when you combine them, you can just use Union and Distinct:
var derp1 = new List<Derp>();
derp1.Add(new Derp() {strName = "X"});
derp1.Add(new Derp() { strName = "Y" });
derp1.Add(new Derp() { strName = "Z" });
var derp2 = new List<Derp>();
derp2.Add(new Derp() {strName = "A"});
derp2.Add(new Derp() { strName = "B" });
derp2.Add(new Derp() { strName = "X" });
var derp3 = new List<Derp>();
derp3.Add(new Derp() { strName = "J" });
derp3.Add(new Derp() { strName = "B" });
derp3.Add(new Derp() { strName = "X" });
var merged = derp1.Union(derp2.Union(derp3)).Distinct();
Console.WriteLine(merged.Count()); // Returns 6: X, Y, Z, A, B, J
I'm currently refactoring code to replace Convert.To's to TryParse.
I've come across the following bit of code which is creating and assigning a property to an object.
List<Person> list = new List<Person>();
foreach (DataRow row in dt.Rows)
{
var p = new Person{ RecordID = Convert.ToInt32(row["ContactID"]) };
list.Add(p);
}
What I've come up with as a replacement is:
var p = new Person { RecordID = Int32.TryParse(row["ContactID"].ToString(), out RecordID) ? RecordID : RecordID };
Any thoughts, opinions, alternatives to what I've done?
Write an extension method.
public static Int32? ParseInt32(this string str) {
Int32 k;
if(Int32.TryParse(str, out k))
return k;
return null;
}
I'd use an alternative implementation TryParse which returns an int?:
public static int? TryParseInt32(string x)
{
int value;
return int.TryParse(x, out value) ? value : (int?) null;
}
Then you can write:
var p = new Person { RecordID = Helpers.TryParseInt32(row["ContactID"].ToString()) ?? 0 };
(Or use a different default value, if you want - either way it'll be visible in your code.)
I suggest separate the TryParse part from initializer. It will be more readable.
int recordId;
Int32.TryParse(row["ContactID"].ToString(), out recordID)
foreach (DataRow row in dt.Rows)
{
var p = new Person{ RecordID = recordId };
list.Add(p);
}
private static void TryToDecimal(string str, Action<decimal> action)
{
if (decimal.TryParse(str, out decimal ret))
{
action(ret);
}
else
{
//do something you want
}
}
TryToDecimal(strList[5], (x) => { st.LastTradePrice = x; });
TryToDecimal(strList[3], (x) => { st.LastClosedPrice = x; });
TryToDecimal(strList[6], (x) => { st.TopPrice = x; });
TryToDecimal(strList[7], (x) => { st.BottomPrice = x; });
TryToDecimal(strList[10], (x) => { st.PriceChange = x; });