2015-10-11 10 views
6

Ho un DataTable con le seguenti informazioni:Linq distinta solo in righe successive corrispondere

365.00 
370.00 
369.59 
365.00 
365.00 -> match with previous item 
365.00 -> match with previous item 

ho solo bisogno di rimuovere gli elementi abbinati prossimi, in questo modo:

365.00 
370.00 
369.59 
365.00 

ho provato:

(from articlespricehistory in dt.AsEnumerable() 
select new 
{ 
    articlepricehistory_cost = articlespricehistory.Field<Double>("articlepricehistory_cost") 
}) 
.DistinctBy(i => i.articlepricehistory_cost) 
.ToList(); 

Risultato:

365.00 
370.00 
369.59 

Qualche idea?

risposta

7

Un altro approccio:

public static IEnumerable<T> MyDistinct<T>(this IEnumerable<T> items) 
{ 
    T previous = default(T); 
    bool first = true; 
    foreach(T item in items) 
    { 
     if (first || !Equals(previous, item)) 
     { 
      first = false; 
      previous = item; 
      yield return item; 
     } 
    } 
} 

Oppure, come richiesto, con un selettore

public static IEnumerable<T> MyDistinct<T, U>(this IEnumerable<T> items, Func<T, U> selector) 
{ 
    U previous = default(U); 
    bool first = true; 
    foreach(T item in items) 
    { 
     U current = selector(item); 
     if (first || !Equals(previous, current)) 
     { 
      first = false; 
      previous = current; 
      yield return item; 
     } 
    } 
} 
+0

Qual è il motivo alla base del fatto che ti impegni a mantenere l'ordine? – displayName

+0

Hy Grazie per la risposta, quindi sai come posso aggiungere un selettore a questa espressione? In questo modo: '.MyDistinct (i => i.articlepricehistory_cost)' perché ho bisogno di confrontare solo una colonna –

+0

@displayName: non capisco la domanda. Puoi chiarirlo? –

1

Il problema nella query è che si sta utilizzando .DistinctBy() che restituirà solo risultati distinti. Quindi, se 365,00 è apparso ovunque, non verrà più visualizzato nell'elenco restituito.

var differentPreviousList = new List<double>(); 
var itemPriceList = dt.ToList(); 
differentPreviousList.Add(itemPriceList[0]); 
for (var index = 1; index < itemPriceList.Count; index++) 
{ 
    if (itemPriceList[index - 1] == itemPriceList[index]) continue; 
    differentPriceList.Add(itemPriceList[index]); 
} 
2

Ecco un'idea che non ho effettivamente provato

  • Fare un Skip(1) sulla query per produrre una seconda query.
  • Aggiunge ora alla seconda query qualsiasi elemento diverso dall'ultimo elemento nella prima query, per produrre una terza query.
  • Ora zip unire la prima e la terza query insieme per formare un set di coppie; questa è la quarta query.
  • Ora costruisci una quinta query che filtra le coppie che hanno elementi identici dalla quarta query.
  • Infine, costruire una sesta query che seleziona il primo elemento di ciascuna coppia dalla quinta query.

La sesta query è il set di dati desiderato.

0

Penso che sia necessario utilizzare un valore temporaneo per verificare se il valore successivo corrisponde o meno al valore corrente.

double temporary = -1; // temp value for checking 
List<double> results = new List<double>(); // list to store results 

(from articlespricehistory in dt.AsEnumerable() 
select new 
{ 
    articlepricehistory_cost = articlespricehistory.Field<Double>("articlepricehistory_cost") 
}) 
    .Select(a => a.articlepricehistory_cost) 
    .ToList() 
    .ForEach(cost => 
    { 
     if (temporary != cost) { results.Add(cost); } 
     temporary = cost; 
    }); 

foreach (var result in results) 
{ 
    Console.WriteLine(result); 
} 

Dopo il metodo ForEach è equivalente al seguente.

foreach (var cost in costs) 
{ 
    if (temporary != cost) 
    { 
     results.Add(cost); 
     Console.WriteLine(cost); 
    } 
    temporary = cost; 
} 
+0

Se si utilizza un comando ForEach contenente istruzioni per modificare una variabile temporanea, perché non modificare semplicemente il ciclo 'foreach' sotto di esso? –

+0

Hai ragione. Il mio codice è equivalente all'utilizzo del ciclo foreach. Ho aggiunto un'altra versione. Ho usato il metodo ForEach solo per evitare di impostare i primi risultati in una nuova lista e come al solito per me. Grazie. – jhmt

3

Ecco una soluzione LINQ pulito per u

var list = (dt as Enumerable); 

var numbers = list.TakeWhile((currentItem, index) => currentItem != list.ElementAtOrDefault(index - 1)); 

tenere a mente se hai 0 come primo elemento che b e ommitted dal nuovo elenco poiché ElementAtOrDefault restituirà 0 nella prima iterazione del ciclo while (indice di -1), valutando quindi l'espressione su false. Una semplice dichiarazione if può aiutarti a evitare questo.

+1

Nota che se devi introdurre una nota che spiega il codice, il codice non è sufficientemente chiaro. Perché non dire semplicemente "TakeWhile ((currentItem, index) => ..." ed eliminare la nota? –

+0

Non ci avevo pensato - editato – kskyriacou

0

Potrebbe non essere una soluzione elegante ma vorrei solo analizzare una query netta ... c'è come.

  1. Eseguire una query lambda per ottenere tutti i risultati originali senza cercare di filtrare DistinctBy.
  2. Creare un oggetto di un risultato di una singola query del tipo per il quale si è richiesto inizialmente la query.
  3. Inizializza un nuovo elenco per i risultati di analisi foreach.
  4. Fare un ciclo per ogni risultato.
  5. La prima se la sezione deve essere se (oggetto sopra il ciclo è nullo).
  6. SE è vero aggiungi articolo alla lista
  7. ELSE se controllare per vedere se il valore dell'articolo è uguale all'ultima iterazione.
  8. Memorizzare l'oggetto iterazione sull'oggetto dichiarato prima del ciclo.

Risciacquo e ripetizione, e il risultato non è nessun oggetto duplicato trovato in una riga nel ciclo verrà memorizzato nella lista risultante in ciò che volevi.

Problemi correlati