2016-01-28 35 views
7

Desidero un metodo come OrderBy() che ordini sempre ignorando le lettere accentate e guardarle come non accentate. Ho già provato a sovrascrivere OrderBy() ma sembra che non posso farlo perché questo è un metodo statico.Ordine Per ignorare le lettere accentate

Così ora voglio creare un'espressione lambda personalizzato per OrderBy(), in questo modo:

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)); 
} 

Tuttavia, sto ottenendo questo errore:

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.

sembra che non gli piace StringComparer . Come posso risolvere questo?

Nota:

ho già cercato di usare RemoveDiacritics() da here ma non so come utilizzare questo metodo in questo caso. Così ho provato a fare qualcosa come this che sembra anche bello.

+0

Si sta utilizzando Linq2Sql o LinqObjects? –

+1

A cosa serve HashSet? – usr

risposta

2

risolto! Stavo ricevendo quell'errore perché per usare StringComparer l'elemento per ordinare nell'espressione OrderBy() quell'elemento deve essere un string.

Quindi, quando so che quell'elemento è una stringa, scrivo su una stringa e io uso il metodo RemoveDiacritics() per ignorare le lettere accentate e guardarle come non accentate.

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())); 
} 

da garantire la RemoveDiacritics() funziona bene aggiungo una linea HtmlDecode().

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

Non è assolutamente necessario implementare il tuo' ToOrderBy'. una enumerazione di stringhe chiamata 'mystrings' puoi semplicemente chiamare' mystrings.OrderBy (RemoveDiacritics, StringComparer.Create (culture, false)) ' –

+0

@ RenéVogt Lo so, ma voglio fare una performance perché il mio progetto è enorme e non voglio per fare questa modifica in tutto il progetto – Ninita

+0

@ RenéVogt per usare 'ToOrderBy()' nella mia soluzione ho solo bisogno di fare Ctrl + F e sostituire '. OrdderBy (' con '.ToOrderBy (' e in futuro se ho bisogno di cambiare qualcosa in questa logica ho solo bisogno di cambiare in un posto e non nell'intero progetto.Qui è la mia performance.Per l'eccezione ho un metodo 'SafeAny()' dove controllo se non è nullo e ho alcun elemento. – Ninita

2

OrderBy prende uno keySelector come primo argomento. Questo keySelector dovrebbe essere un Func<string,T>. Quindi è necessario un metodo che accetta una stringa e restituisce un valore in base al quale l'elenco deve essere ordinato.

Sfortunatamente non sono sicuro di come determinare se un personaggio è una "lettera accentata". Il RemoveDiacritics non funziona per il mio é.

Quindi supponiamo di avere un metodo chiamato IsAccentedLetter che determina se un personaggio è una lettera accentata:

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

in modo da poter ordinare l'elenco del genere:

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)); 

L'espressione lambda prende una stringa e restituisce la stessa stringa, ma ha sostituito le lettere accentate con uno spazio vuoto.
OrderBy ora ordina l'enumerazione con queste stringhe, quindi "ignora" le lettere accentate.

UPDATE: Se si dispone di un metodo di lavoro RemoveDiacritics(string s) che restituisce le corde con le lettere accentate sostituito come si desidera, si può semplicemente chiamare OrderBy come questo:

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

quella logica mi dà l'errore ''TSource' non contiene una definizione per 'Seleziona' e nessun metodo di estensione 'Seleziona' accettando un primo argomento di tipo 'TSource' potrebbe essere trovato (ti manca una direttiva using o un riferimento assembly ?) '. Inoltre stai sostituendo la lettera accentata con uno spazio bianco e voglio che la lettera accentata sia tratteggiata come una lettera non accentata. – Ninita

+0

@Ninita oh questo era un malinteso: la mia "fonte" doveva essere la tua lista originale. Non intendevo implementare la mia estensione 'ToOrderBy', non è necessario! Nella tua soluzione, non hai bisogno di 'ToOrderBy', puoi semplicemente chiamare' mystrings.OrderBy (RemoveDiacritics, StringComparer, Create (culture, false) ' –