2016-01-28 25 views
7

Chcę taką metodę, jak OrderBy(), która zawsze zamawia ignorowanie akcentowanych liter i patrzenie na nie jak bez akcentu. Próbowałem już przesłonić OrderBy(), ale wydaje się, że nie mogę tego zrobić, ponieważ jest to metoda statyczna.OrderBy ignorowanie akcentowanych liter

Więc teraz chcę utworzyć wyrażenie lambda dla OrderBy() zwyczaj, tak:

public static IOrderedEnumerable<TSource> ToOrderBy<TSource, TKey>(
    this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) 
{ 
    if(source == null) 
     return null; 

    var seenKeys = new HashSet<TKey>(); 

    var culture = new CultureInfo("pt-PT"); 
    return source.OrderBy(element => seenKeys.Add(keySelector(element)), 
          StringComparer.Create(culture, false)); 
} 

Jednak dostaję ten błąd:

Error 2 The type arguments for method 'System.Linq.Enumerable.OrderBy<TSource,TKey>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,TKey>, System.Collections.Generic.IComparer<TKey>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

wydaje się to nie podoba StringComparer . Jak mogę to rozwiązać?

Uwaga:

Próbowałem już używać RemoveDiacritics() z here ale nie wiem, jak korzystać z tej metody w tym przypadku. Więc próbowałem zrobić coś takiego, jak this, co również wydaje się fajne.

+0

Czy używasz Linq2Sql lub LinqObjects? –

+1

Do czego służy HashSet? – usr

Odpowiedz

2

rozwiązany! Otrzymałem ten błąd, ponieważ aby użyć elementu StringComparer do sortowania w wyrażeniu OrderBy(), ten element musi być string.

Kiedy więc wiem, że element jest ciągiem, rzucam na napis i używam metody RemoveDiacritics(), aby zignorować akcentowane litery i patrzeć na nie jak z akcentem.

public static IOrderedEnumerable<TSource> ToOrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) 
{ 
    if(!source.SafeAny()) 
     return null; 

    return source.OrderBy(element => Utils.RemoveDiacritics(keySelector(element).ToString())); 
} 

Aby Gwarantujemy RemoveDiacritics() działa dobrze dodaję HtmlDecode() linię.

public static string RemoveDiacritics(string text) 
{ 
    if(text != null) 
     text = WebUtility.HtmlDecode(text); 

    string formD = text.Normalize(NormalizationForm.FormD); 
    StringBuilder sb = new StringBuilder(); 

    foreach (char ch in formD) 
    { 
     UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(ch); 
     if (uc != UnicodeCategory.NonSpacingMark) 
     { 
      sb.Append(ch); 
     } 
    } 

    return sb.ToString().Normalize(NormalizationForm.FormC); 
} 
+1

Nie ma absolutnie potrzeby implementacji' ToOrderBy'. wyliczanie ciągów o nazwie 'mystrings' można po prostu nazwać' mystrings.OrderBy (RemoveDiacritics, StringComparer.Create (culture, false)) ' –

+0

@ RenéVogt Wiem, ale chcę tego występu, ponieważ mój projekt jest ogromny i nie chcę aby wykonać tę modyfikację w całym projekcie – Ninita

+0

@ RenéVogt, aby użyć 'ToOrderBy()' w moim rozwiązaniu, wystarczy wykonać Ctrl + F i zastąpić '.OrderBy (' z '.ToOrderBy (' iw przyszłości, jeśli potrzebuję zmienić coś w tej logice, którą muszę zmienić tylko w jednym miejscu, a nie w całym projekcie, oto moja wydajność, dla wyjątku mam metodę 'SafeAny()', w której sprawdzam, czy nie jest ona zerowa i ma dowolny element. – Ninita

2

OrderBy przyjmuje keySelector jako pierwszy argument. Ten keySelector powinien być Func<string,T>. Potrzebujesz więc metody, która pobiera ciąg znaków i zwraca wartość , dzięki której twoje wyliczenie powinno zostać posortowane na.

Niestety nie jestem pewien, jak ustalić, czy postać jest "literą akcentowaną". Numer RemoveDiacritics nie działa w przypadku mojego é.

Więc załóżmy masz metodę zwaną IsAccentedLetter, która określa, czy postać jest akcentowany list:

public bool IsAccentedLetter(char c) 
{ 
    // I'm afraid this does NOT really do the job 
    return CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.NonSpacingMark; 
} 

Więc można posortować listę tak:

string[] myStrings = getStrings(); // whereever your strings come from 
var ordered = myStrings.OrderBy(s => new string(s.Select(c => 
    IsAccentedLetter(c) ? ' ' : c).ToArray()), StringComparer.Create(culture, false)); 

Wyrażenie lambda trwa ciąg znaków i zwraca ten sam ciąg, ale zastępuje akcentowane litery pustym polem.
OrderBy teraz sortuje twoje wyliczenie według tych ciągów, a więc "ignoruje" akcentowane litery.

UPDATE: Jeśli masz metodę pracy RemoveDiacritics(string s) że zwraca struny akcentowane litery zastępowane jak chcesz, możesz po prostu zadzwonić OrderBy takiego:

string[] mystrings = getStrings(); 
var ordered = myStrings.OrderBy(RemoveDiacritics, StringComparer.Create(culture, false)); 
+0

logika daje mi błąd "TSource" nie zawiera definicji "Wybierz" i nie można znaleźć metody rozszerzenia "Wybierz" akceptując pierwszy argument typu "TSource" (czy brakuje dyrektywy użycia lub odniesienia do zespołu ?) '. Poza tym zastępujesz literę akcentowaną białą spacją i chcę, aby litera akcentowana była traktowana jak list nieakcentowany. – Ninita

+0

@Ninita oh to było nieporozumienie: moje "źródło" miało być twoją oryginalną listą. W ogóle nie zamierzałem implementować mojego własnego rozszerzenia 'ToOrderBy', nie jest to konieczne! W twoim rozwiązaniu nie potrzebujesz 'ToOrderBy', możesz po prostu zadzwonić' mystrings.OrderBy (RemoveDiacritics, StringComparer, Create (kultura, fałsz) ' –