With respect to How to cast while Sorting?, I tried one of the answers below.
vehicleList.OrderBy(c=>
{
Car car = c as Car;
if (car != null)
return car.ModelName
else
return "";
}
But it gives compiler error:
The type arguments for method
'System.Linq.Enumerable.OrderBy(System.Collections.Generic.IEnumerable,
System.Func)' cannot be inferred from the usage. Try
specifying the type arguments explicitly.
What is wrong in this? How to correct?
What I try to achieve is as below:
Sort by ModelName if the object is of type Car
Else if object id of type Train, then by TrainName.
This solves the error:
var sortedVehicles = vehicleList.OrderBy<Vehicle, string>(c=>
{
...
});
You can ipmplement a comparer like this:
var sorted = uiItems.OrderBy(x => x.Contact, new YourComparer ());
public class YourComparer : IComparer<Contact>
{
public int Compare(Contact? x, Contact? y)
{
if (x == y)
return 0;
if (x == null)
return 1;
if (y == null)
return -1;
if (x.Presence == null)
return 1;
if (y.Presence == null)
return -1;
return return x.Presence < y.Presence ? -1 : 1;
}
}
Did you try the suggestion in the question you referenced, to use a Linq Cast?
var sorted = uiItems.Cast<Contact>()
.OrderBy(c => contact.Presence; );
Look at the below example:
class Item
{
public int X;
public string Y;
}
var items = new Item[100];
var ordered = items.OrderBy<Item, string>(i => i.Y);
OrderBy requires two types. The former is of the enumerable and the latter is used in lambda expression. Additionaly in some cases, the elements of collection that is being used, must be casted. You can use Cast method prior to OrderBy if you are sure, that all types are of a particular type or OfType in order to ommit other types.
Related
I have a custom class called PairString
public class PairString: IComparer<PairString>
{
public string first;
public string second;
public PairString(string f, string s)
{
first = f;
second = s;
}
public int Compare([AllowNull] PairString x, [AllowNull] PairString y)
{
if (x == null || y == null) return -1;
var f = string.Compare(x.first, y.first);
var s = string.Compare(x.second, y.second);
return f == s ? s : f;
}
}
I want to create groups by count and then by lexical order of strings in that groups, from a list of input PairString List. Below method does the grouping right. But when I try to sort the groups in lexical order for equal count groups, it throws "Atleast one object must implement IComparer error"
public static List<string> MaxItemAssociatoinGroup(List<PairString> input)
{
if (input == null || input.Count == 0) return null;
List<SortedSet<string>> output = new List<SortedSet<string>>();
foreach (var item in input)
{
if (output.Any(x => x.Contains(item.first) || x.Contains(item.second)))
{
//Take the set containing one or two or both items
var set1 = output.FirstOrDefault(x => x.Contains(item.first));
var set2 = output.FirstOrDefault(x => x.Contains(item.second));
if (set1 == null)
set2.UnionWith(new SortedSet<string> { item.first, item.second });
else if (set2 == null)
set1.UnionWith(new SortedSet<string> { item.first, item.second });
else if (set1 != set2)
{
set1.UnionWith(set2);
output.Remove(set2);
}
}
else
output.Add(new SortedSet<string>(new List<string>() { item.first, item.second }));
}
var maxlistAssociation = output.OrderByDescending(x => x.Count).First();
return new List<string>(maxlistAssociation);
}
I am not sure how to achieve lexical order for same count groups,
Sample input is
new PairString("item3","item4"),
new PairString("item3","item6"),
new PairString("item5","item6"),
new PairString("item2","item8"),
new PairString("item8","item9"),
new PairString("item1","item2")
it groups into 2 groups of equal count {item3,item4,item5,item6} & {item1,item2,item8,item9} but returns {item3,item4,item5,item6} as its first in the list. but I want the second group as it contains the item that lexicographically first than first group. what am I missing here?
It appears that you're missing a method that will compare two SortedSet<string> objects and return the one which comes first lexically. One way to do this is to compare each item from one set with the corresponding one in the other set, and return the first non-equal comparison:
public class SortedSetComparer<T> : IComparer<SortedSet<T>> where T : IComparable<T>
{
public int Compare(SortedSet<T> x, SortedSet<T> y)
{
// Null checks
if (x == null) return y == null ? 0 : 1;
if (y == null) return -1;
var minCount = Math.Min(x.Count, y.Count);
// Compare each item from one set with the corresponding one in the other set
for (var i = 0; i < minCount; i++)
{
var result = x.ElementAt(i).CompareTo(y.ElementAt(i));
// Return the first non-equal result
if (result != 0) return result;
}
// If all the items were equal, return the comparison of the Count
return x.Count.CompareTo(y.Count);
}
}
Then we can order our results (after sorting by size) by passing an instance of this class to the ThenBy method:
var maxlistAssociation = output
.OrderByDescending(x => x.Count)
.ThenBy(x => x, new SortedSetComparer<string>())
.First();
Depending on the behavior you want from this method, we could also incorporate the ordering by Count into our comparison method, so that it puts the sets with the most items first, then sorts them alphabetically:
public class SortedSetComparer<T> : IComparer<SortedSet<T>> where T : IComparable<T>
{
public int Compare(SortedSet<T> x, SortedSet<T> y)
{
// Null checks
if (x == null) return y == null ? 0 : 1;
if (y == null) return -1;
// Compare the counts first, in descending order
var countComparison = x.Count.CompareTo(y.Count);
if (countComparison != 0) return countComparison * -1;
// Then compare each item from one set lecially
// with the corresponding one in the other set
return x.Select((item, index) =>
x.ElementAt(index).CompareTo(y.ElementAt(index)))
.FirstOrDefault(result => result != 0);
}
}
And now we only need one OrderBy clause:
var maxlistAssociation = output
.OrderBy(x => x, new SortedSetComparer<string>())
.First();
I have a big array of 700 groups. I need to sort the array by specific rules:
We need to sort by company name
Hierarchy depends on the number of points in the groups.Name
There are a lot of records with same company name, but the first one to show from specific company must contain ".All". After this record, we need to put all others with the same name ordered by "1."
Specific case when there is a position directly applied to the company
Example:
groups[0].CompanyName = "Acompany"
groups[1].CompanyName = "Acompany"
groups[2].CompanyName = "Acompany"
groups[3].CompanyName = "Acompany"
groups[4].CompanyName = "Acompany"
groups[5].CompanyName = "Bcompany"
groups[6].CompanyName = "Bcompany"
groups[7].CompanyName = "Bcompany"
groups[0].Name = "Acompany.All" //(root)
groups[1].Name = "D.Acompany.example" //this is the specific case (leaf)
groups[2].Name = "Acompany.ABC"//(group)
groups[3].Name = "D.Acompany.ABC.PrimaryTeacher" //(leaf)
groups[4].Name = "Acompany.ABC.Something"//(group)
groups[5].Name = "Bcompany.All" //(root)
groups[6].Name = "Bcompany.Sites"//(group)
groups[7].Name = "Bcompany.Sites.example" //(leaf)
The example shows how the array should look like after sort. It's really complicated, I hope I have managed to explain it.
For now I have achieved:
There are 2 problems :
1.D.A.1stFloor.Cleaner must be under A.1stFloor
2.D.B.Society.Worker must be under B.Society
My code for now :
Array.Sort(groups, (a, b) =>
{
if (a.CompanyName != b.CompanyName)
{
return a.CompanyName.CompareTo(b.CompanyName);
}
if (a.Name.Contains(".All"))
{
return -1;
}
if (b.Name.Contains(".All"))
return 1;
if (a.Name.StartsWith("D.") && a.Name.Count(x => x == '.') == 2)
return -1;
if (b.Name.StartsWith("D.") && b.Name.Count(x => x == '.') == 2)
return 1;
if (a.Name.Count(x => x == '.') == 1)
return -1;
if (b.Name.Count(x => x == '.') == 1)
return 1;
if (a.Name.StartsWith("D") && a.Name.Count(x => x == '.') == 3) //needs to be moved I guess
return -1;
if (b.Name.StartsWith("D") && b.Name.Count(x => x == '.') == 3)//needs to be moved I guess
return 1;
return a.Name.CompareTo(b.Name);
});
You construct each company as a tree, like the image below for ACompany, base on your rules and string processing functions:
Then you just need to use a Depth-First Tree traversal algorithm to get the order.
I cannot imagine how complicated your sorting rules is, sorry. But, I would like to suggest you to take advantage of existing sorting function.
Construct each group as an object, I believe that you've done that:
public class Company
{
public string CompanyName { get; set; }
public string Name { get; set; }
}
So, your group is just an array or list of Companies.
var group = new List<Company>();
Then, you implement your sorting rules in your defined comparer:
public class CoordinatesBasedComparer : IComparer<Company>
{
public int Compare(Company a, Company b)
{
//your sorting rules implemented here
}
}
Finally, you just call:
var comparer = new CoordinatesBasedComparer();
group.Sort(comparer);
Hope this helps.
Your rules are not entirely clear, but here's an example of sorting with a comparator:
Array.Sort(groups, (a, b) =>
{
// highest priority rule
if (a.CompanyName != b.CompanyName)
{
return a.CompanyName.CompareTo(b.CompanyName); // or a custom comparison
}
// more rules ordered by priority
if (a.Name == a.CompanyName + ".All")
return -1; // => "a" comes first
if (b.Name == b.CompanyName + ".All")
return 1; // => "b" comes first
// more rules...
// default rule
return a.Name.CompareTo(b.Name);
});
I am trying to find the number of items in a list that differ in a property which itself is a list. I found this example using Linq here:
List<Person> distinctPeople = allPeople
.GroupBy(p => p.PersonId)
.Select(g => g.First())
.ToList();
This works nicely if the property PersonId is scalar. But in my case this does not work and in the following the items in SelectedTargets are always returned as distinct even though the ListOfActions is equal in all items:
List<Target> distinctTargets = SelectedTargets.GroupBy(p => p.ListOfActions).Select(g => g.First()).ToList();
If instead I pick the first item in ListOfActions it works:
List<Target> distinctTargets = SelectedTargets.GroupBy(p => p.ListOfActions[0]).Select(g => g.First()).ToList();
So how can I check for equality of the whole list ListOfActions? (it doesn't necessarily have to user Linq)
The definition of SelectedTargets is:
List<Target> SelectedTargets = new List<Target>();
and is DispensingActionList:
private DispensingActionList ListOfActions = new DispensingActionList();
public class DispensingActionList : List<DispensingAction>
{ ...
You could use a custom IEqualityComparer<T> for the GroupBy overload which compares sequences. For example this which uses Enumerable.SequenceEqual:
public class SequenceComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
var comparer = EqualityComparer<T>.Default;
return x.SequenceEqual(y, comparer);
}
public int GetHashCode(IEnumerable<T> items)
{
unchecked
{
int hash = 17;
foreach (T item in items)
{
hash = hash * 23 + (item == null ? 0 : item.GetHashCode());
}
return hash;
}
}
}
Now this should work:
List<Target> distinctTargets = SelectedTargets
.GroupBy(p => p.ListOfActions, new SequenceComparer<DispensingAction>())
.Select(g => g.First())
.ToList();
Of course DispensingAction also needs to override Equals to compare the objects meaningfully and not only checks whether they're the same reference or not.
You could use Enumerable.SequenceEqual<TSource>
You will have to override the GetHashCode and Equals methods for your types if you didn't do so yet.
I have collection of items which are having one enum property list.
Original property looks like
public class Content {
List<State> States {get; set;}
}
where 'State' is enum with almost 15 options.
While iterating collection of Content objects, I want to check it States property has certain values like State.Important and State.Updated exists in States and set another string from it.
something like
if(item.States.Has(State.Important) && item.States.Has(State.Updated))
string toProcess = "Do";
How to do this using Linq or Lambda ?
This should work if you must use Linq:
if (item.States.Any(state => state == State.Important) && item.States.Any(state => state == State.Updated))
Otherwise just use Contains() like #ElRonnoco says.
(However if your states are flags (powers of 2), then this answer will be slightly different.)
The trouble with this approach is that it iterates over the collection fully twice if neither of the states are set. If this happens often, it will be slower than it could be.
You can solve it without linq in a single pass like so:
bool isUpdated = false;
bool isImportant = false;
foreach (var state in item.States)
{
if (state == State.Important)
isImportant = true;
else if (state == State.Updated)
isUpdated = true;
if (isImportant && isUpdated)
break;
}
if (isImportant && isUpdated)
{
// ...
}
This is unlikely to be an issue unless you have very large lists which often don't have either of the target states set, so you're probably best off using El Ronnoco's solution anyway.
If you have a lot of states to deal with, you could simplify things by writing an extension method like so:
public static class EnumerableExt
{
public static bool AllPredicatesTrueOverall<T>(this IEnumerable<T> self, params Predicate<T>[] predicates)
{
bool[] results = new bool[predicates.Length];
foreach (var item in self)
{
for (int i = 0; i < predicates.Length; ++i)
if (predicates[i](item))
results[i] = true;
if (results.All(state => state))
return true;
}
return false;
}
I had some difficulty coming up for a name for this. It will return true if for each predicate there is at least one item in the sequence for which the predicate is true. But that's a bit long for a method name... ;)
Then your example would become:
if (item.States.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
Here's some sample code that uses it:
enum State
{
Unknown,
Important,
Updated,
Deleted,
Other
}
void run()
{
IEnumerable<State> test1 = new[]
{
State.Important,
State.Updated,
State.Other,
State.Unknown
};
if (test1.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
Console.WriteLine("test1 passes.");
else
Console.WriteLine("test1 fails.");
IEnumerable<State> test2 = new[]
{
State.Important,
State.Other,
State.Other,
State.Unknown
};
if (test2.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
Console.WriteLine("test2 passes.");
else
Console.WriteLine("test2 fails.");
// And to show how you can use any number of predicates:
bool result = test1.AllPredicatesTrueOverall
(
state => state == State.Important,
state => state == State.Updated,
state => state == State.Other,
state => state == State.Deleted
);
}
But perhaps the easiest is to write an extension method for IEnumerable<State> (if you only have the one state enum to worry about):
public static class EnumerableStateExt
{
public static bool AllStatesSet(this IEnumerable<State> self, params State[] states)
{
bool[] results = new bool[states.Length];
foreach (var item in self)
{
for (int i = 0; i < states.Length; ++i)
if (item == states[i])
results[i] = true;
if (results.All(state => state))
return true;
}
return false;
}
}
Then your original code will become:
if (item.States.AllStatesSet(State.Important, State.Updated))
and you can easily specify more states:
if (item.States.AllStatesSet(State.Important, State.Updated, State.Deleted))
You don't need Linq. I don't thinq
if(item.States.Contains(State.Important) && item.States.Contains(State.Updated))
string toProcess = "Do";
http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx
List has a Contains method, so your code would be
if(item.States.Contains(State.Important) && item.States.Contains(State.Updated))
string toProcess = "Do";
I see no real benefit in using Linq or a lambda expression here...
You could go with
!(new List<States>{State.Important, State.Updated}.Except(item.States).Any());
It's not really shorter, but easier if you have a huge number of states to check.
As long as you want to check that the item has all states needed, you just have to add new States to the first list.
var res = (from items in item
where items.States.Has(State.Important) && items.States.Has(State.Updated)
select new { NewProcess = "Do" }).ToList();
foreach (var result in res)
{
string result = result.NewProcess
}
Try this
Maybe you could consider using your enum as a set of flags, i.e. you can combine multiple states without having a list:
[Flags]
public enum State
{
Important = 1,
Updated = 2,
Deleted = 4,
XXX = 8
....
}
public class Content
{
public State MyState { get; set; }
}
if ((myContent.MyState & State.Important) == State.Important
&& (myContent.MyState & State.Updated) == State.Updated)
{
// Important AND updated
}
Some sort of following implementation
Content obj = new Content();
obj.States = SomeMethod();
if(obj.States.Any(h => h == State.Important) && obj.States.Any(h => h == State.Updated))
{
string toProcess = "Do";
}
I know that this can be rewritten using a lambda expression. But I cant seem to figure it out. does anyone have an opinion on how it should be written using a lambda.
foreach (var _systemItem in _systemData)
{
foreach (var _institutionItem in _institutionData)
{
if (_systemItem.LookupValue == _institutionItem.OriginalSystemLookupValue)
{
_values.Add(new LookupValue()
{
DisplayText = _institutionItem.LookupText,
Value = _institutionItem.LookupValue
});
}
else
{
_values.Add(new LookupValue()
{
DisplayText = _systemItem.LookupText,
Value = _systemItem.LookupValue
});
}
}
}
Like this:
values.AddRange(from s in _systemData
from i in institutionData
select s.LookupValue == i.OriginalSystemLookupValue ?
new LookupValue {
DisplayText = _institutionItem.LookupText,
Value = _institutionItem.LookupValue
}
: new LookupValue {
DisplayText = _systemItem.LookupText,
Value = _systemItem.LookupValue
}
);
Is _values a List<LookupValue> which is empty to start with? If so, that look might look like this:
_values = (from x in _systemData
from y in _institutionData
let item = x.LookupValue == y.OriginalSystemLookupValue ? x : y
select new LookupValue { DisplayText = item.LookupText,
Value = item.LookupValue })
.ToList();
That assumes that _systemItem and _institutionItem are the same type. If they're unrelated types, you might want to give them a common interface that defines LookupText and LookupValue (or even a ToLookupValue method) and then cast one of the operands in the conditional operator to the interface. For example:
_values = (from x in _systemData
from y in _institutionData
let item = x.LookupValue == y.OriginalSystemLookupValue
? (ILookupSource) x : y
select item.ToLookupValue())
.ToList();
Sure, I have an opinion. I'd write it like this:
var pairs = _systemData.SelectMany(s =>
_institutionData.Select(i => new { System = s, Institution = i }));
_values.AddRange(pairs.Select(x =>
{
bool match = x.System.LookupValue == x.Insitution.OriginalSystemLookupValue;
return match ? new LookupValue(x.Institution) : new LookupValue(x.System);
}));
And move the object initializers for LookupValue into real constructors that take an Institution or System.