I have to perform on some collection and return ranking based on some logic I wish to optimize this working code since I think it can be bettered (maybe using tasks?).
I need to search on Counterpart
s item. I load this data from DB and I've got more aliases per counterpart.
I need to return the result based on those criteria:
Counterpart
Code
is equal to search stringCounterpart
Code
starts with search stringCounterpart
Code
contains search stringCounterpart
Description
contains search stringCounterpart
Alias
is equal to search stringCounterpart
Alias
starts with search stringCounterpart
Alias
contains search string
Each of those rules starts from Ranking 1 to 7 and I have to sort ok that ranking ascending.
public class Counterpart
{
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public IEnumerable<Alias> Aliases { get; set; }
public override bool Equals(object obj)
{
Counterpart obj2 = obj as Counterpart;
if (obj2 == null) return false;
return Id == obj2.Id;
}
}
public class Alias
{
public int? Type { get; set; }
public string Description { get; set; }
}
internal class CounterPartRanking
{
public int Rank { get; set; }
public Counterpart CounterPart { get; set; }
}
public static class CounterpartExtensions
{
public static IEnumerable<Counterpart> SearchWithRank(this IEnumerable<Counterpart> source, string pattern)
{
var items1 = source.Where(x => x.Code == pattern);
var items2 = source.Where(x => x.Code.StartsWith(pattern));
var items3 = source.Where(x => x.Code.Contains(pattern));
var items4 = source.Where(x => x.Description.Contains(pattern));
var items5 = source.Where(x => x.Aliases != null && x.Aliases.Any(y => y.Description == pattern));
var items6 = source.Where(x => x.Aliases != null && x.Aliases.Any(y => y.Description.StartsWith(pattern)));
var items7 = source.Where(x => x.Aliases != null && x.Aliases.Any(y => y.Description.Contains(pattern)));
Stopwatch sw = Stopwatch.StartNew();
var rankedItems = new List<CounterPartRanking>();
if (items1.Any())
rankedItems.AddRange(items1.Select(x => new CounterPartRanking { Rank = 1, CounterPart = x }));
if (items2.Any())
rankedItems.AddRange(items2.Select(x => new CounterPartRanking { Rank = 2, CounterPart = x }));
if (items3.Any())
rankedItems.AddRange(items3.Select(x => new CounterPartRanking { Rank = 3, CounterPart = x }));
if (items4.Any())
rankedItems.AddRange(items4.Select(x => new CounterPartRanking { Rank = 4, CounterPart = x }));
if (items5.Any())
rankedItems.AddRange(items5.Select(x => new CounterPartRanking { Rank = 5, CounterPart = x }));
if (items6.Any())
rankedItems.AddRange(items6.Select(x => new CounterPartRanking { Rank = 6, CounterPart = x }));
if (items7.Any())
rankedItems.AddRange(items7.Select(x => new CounterPartRanking { Rank = 7, CounterPart = x }));
sw.Stop();
Debug.WriteLine("Time elapsed {0} for {1}", sw.Elapsed, pattern);
var items = rankedItems.OrderBy(x => x.Rank).Select(x => x.CounterPart);
var distinct = items.Distinct();
return distinct;
}
}
1 Answer 1
Distinct
Take a look at the documentation for Distinct.
A couple things from the remarks should stand out for you:
The Distinct(IEnumerable) method returns an unordered sequence that contains no duplicate values.
I know that the order of the sequence is preserved when using Distinct but that's an implementation detail - there's no gaurantee.
The default equality comparer, Default, is used to compare values of the types that implement the IEquatable generic interface. To compare a custom data type, you must implement this interface and provide your own GetHashCode and Equals methods for the type.
Your custom type Counterpart
does not do this. It should implement IEquatable<Counterpart>
and additionally override both GetHashCode
and Equals
.
public class CounterPart : IEquatable<Counterpart>
{
// ...
public bool Equals(Counterpart other)
{
// left for you.
}
public override bool Equals(object other)
{
return Equals(other as Counterpart);
}
public override bool GetHashCode()
{
// http://stackoverflow.com/a/263416/1402923
}
}
Now you have a type that is safe for use with Distinct
!
AddRange
As far as I know, AddRange
only throws when the collection is null. You don't need to check for items before you call it. That elminates a whole heap of code:
rankedItems.AddRange(items1.Select(x => new CounterPartRanking { Rank = 1, CounterPart = x }));
rankedItems.AddRange(items2.Select(x => new CounterPartRanking { Rank = 2, CounterPart = x }));
rankedItems.AddRange(items3.Select(x => new CounterPartRanking { Rank = 3, CounterPart = x }));
rankedItems.AddRange(items4.Select(x => new CounterPartRanking { Rank = 4, CounterPart = x }));
rankedItems.AddRange(items5.Select(x => new CounterPartRanking { Rank = 5, CounterPart = x }));
rankedItems.AddRange(items6.Select(x => new CounterPartRanking { Rank = 6, CounterPart = x }));
rankedItems.AddRange(items7.Select(x => new CounterPartRanking { Rank = 7, CounterPart = x }));
Other comments
As I mentioned previously, this isn't gauranteed to be correct (but is in every instance I know of):
var items = rankedItems.OrderBy(x => x.Rank).Select(x => x.CounterPart);
var distinct = items.Distinct();