2015-08-24 12 views
6

Ho due oggetti e cioè carta e transazione:filtro due liste in una proprietà C# utilizzando LINQ

Card: 
public string CardID {get; set;} 
public string TransactionRef {get; set;} 

Transaction: 
public string TxnID {get; set;} 
public string TxnDetails {get; set;} 

Nota: Il TransactionRef è del formato Date|TxnID

Ho anche un elenco dei due oggetti List<Card> cardDetails e List<Transaction> transDetails

cardDetails: 
{CardID = '1', TransactionRef = '20150824|Guid1'} 
{CardID = '2', TransactionRef = '20150824|Guid2'} 
{CardID = '3', TransactionRef = '20150824|Guid3'} 

transDetails: 
{TxnID = '23', TxnDetails = 'Guid1'} 
{TxnID = '24', TxnDetails = 'Guid2'} 

Desidero filtrare cardDetails utilizzando transDetails basato su TxnDetails in modo da filtrare gli elementi che non contengono TxnDetails dalla seconda lista.

questo dovrebbe essere l'output:

cardDetails: 
{CardID = '3', TransactionRef = '20150824|Guid3'} 

Ho cercato in questo modo utilizzando LINQ:

cardDetails = cardDetails.Where(x => transDetails.Any(y => x.TransactionRef.Contains(y.TxnDetails) == false)).ToList(); 

ma restituisce sempre la lista come vuoto. Ho provato molte varianti di questa query senza successo. So che questa domanda è stata fatta prima e dopo averli cercati e provato le loro soluzioni, non sono ancora in grado di farlo bene.

Qualcuno può suggerire cosa c'è di sbagliato nella mia richiesta?

Nota: Una cosa che ho dimenticato di menzionare è che queste liste possono contenere migliaia di record. Quindi anche le prestazioni sono importanti.

+0

Questa query dovrebbe effettivamente restituire tutte le carte, sei sicuro che nessuno ritorna? –

+0

Sì, non restituisce nulla. Voglio restituire solo la terza voce in cardDetails, che non contiene TxnDetails dal secondo elenco – nitinvertigo

+0

Perché 'TxnDetails.ToString()' quando 'TxnDetails' È una stringa? –

risposta

5

Questo dovrebbe farlo

var cards = 
    from card in cardDetails 
    let txnDetails = GetTxnDetails(card) 
    where ! transDetails.Any(t => t.TxnDetails == txnDetails) 
    select card; 


static string GetTxnDetails(Card card) 
{ 
    return card.TransactionRef.Split('|')[1]; 
} 

Fiddle: https://dotnetfiddle.net/b9ylFe


Un modo per ottimizzare questo un po 'potrebbe essere quella di memorizzare tutti i possibili dettagli della transazione in un hash set in anticipo. La ricerca dovrebbe quindi essere abbastanza vicina a O (1) (presupponendo una discreta distribuzione di hashcode) invece di O (n) - portando la complessità complessiva dell'algoritmo da O (n * k) a O (n + k).

var allTxnDetails = new HashSet<string>(transDetails.Select(t => t.TxnDetails)); 

var cards = 
    from card in cardDetails 
    let txnDetails = GetTxnDetails(card) 
    where ! allTxnDetails.Contains(txnDetails) 
    select card; 

Fiddle: https://dotnetfiddle.net/hTYCbj

+1

Si potrebbe usare anche 'EndsWith()' in questa particolare situazione –

+0

@CallumLinington Questo è un buon punto – dcastro

1

ne dici di questo?

var results = cardDetails.Where(
    card => !transDetails.Any(
     trans => card.TransactionRef.EndsWith("|" + trans.TxnDetails))); 

demo completa:

using System; 
using System.Linq; 

namespace Demo 
{ 
    class Card 
    { 
     public string CardID; 
     public string TransactionRef; 
    } 

    class Transaction 
    { 
     public string TxnID; 
     public string TxnDetails; 
    } 

    internal class Program 
    { 
     private static void Main() 
     { 
      var cardDetails = new[] 
      { 
       new Card {CardID = "1", TransactionRef = "20150824|Guid1"}, 
       new Card {CardID = "2", TransactionRef = "20150824|Guid2"}, 
       new Card {CardID = "3", TransactionRef = "20150824|Guid3"} 
      }; 

      var transDetails = new[] 
      { 
       new Transaction {TxnID = "23", TxnDetails = "Guid1"}, 
       new Transaction {TxnID = "24", TxnDetails = "Guid2"} 
      }; 

      var results = cardDetails.Where(card => !transDetails.Any(trans => card.TransactionRef.EndsWith("|" + trans.TxnDetails))); 

      foreach (var item in results) 
       Console.WriteLine(item.CardID + ": " + item.TransactionRef);  
     } 
    } 
} 
1

è solo un problema tra parentesi, il == false dovrebbe venire dopo )) non il primo closing.

cardDetails = cardDetails.Where(x => transDetails.Any(y => x.TransactionRef.Contains(y.TxnDetails)) == false).ToList(); 

Causa con il codice effettivo, basta fare l'opposto di quello che vuoi!

si può anche fare

cardDetails = cardDetails.Where(x => !transDetails.Any(y => x.TransactionRef.Contains(y.TxnDetails))).ToList(); 

o qualsiasi improvment suggerito, ma il codice è fondamentalmente molto vicino dal corretto;)

+0

Sì hai ragione! :) l'ho cambiato e funziona. Grazie – nitinvertigo

2

Questa query dovrebbe fare il trucco:

// Get all card details whose transactionrefs don't contain txndetails from the second list 
cardDetails.Where(cd => transDetails.All(ts => !cd.TransactionRef.EndsWith(ts.TxnDetails))) 
    .ToList(); 

Ma c'è qualche ragione specifica per cui stai combinando due pezzi di dati in un campo? Suggerisco di suddividere il campo TransactionRef nella classe Card in due campi: TransactionDate e TransactionID per evitare la manipolazione delle stringhe nelle query.

+1

Sì, questa è la soluzione più semplice. In realtà, il problema è ** Qualsiasi ** dovrebbe cambiare in ** Tutto **. – daniel

+0

@SaedAmini attualmente sto recuperando tutti i dati dal database azzurro e viene fornito solo in questo formato. La mia principale preoccupazione è come saranno le prestazioni per dire 100K voci nella lista – nitinvertigo

0

Se la prestazione è importante, suggerisco di assegnare prima una scheda di classe una proprietà che restituisce la parte dopo il '|' carattere. A seconda della frequenza con cui si desidera eseguire questa query rispetto alla frequenza con cui si costruisce una scheda, potrebbe anche essere il wist lasciare che il costruttore separi il transactionRef in una parte prima di '|' e una parte dopo il '|'.

Qualsiasi metodo scelto non è importante per la query. Facciamo Carta classe supponiamo che ha una proprietà:

string Guid {get {return ...;} 

Capisco che si desidera una sequenza di tutte le carte dal carddetails sequenza che non hanno un GUID che è uguale a uno qualsiasi dei TxnDetails delle transazioni nella sequenza di transDetails .

O in altre parole: se dovessi fare una sequenza di tutti i guids usati in TxnDetails, vuoi che tutte le Carte in CardDetails abbiano un guid che non è nella sequenza di tutti i guids usati.

È possibile utilizzare Any() per questo, ma ciò significherebbe che è necessario cercare la sequenza transDetails per ogni scheda che si desidera controllare.

Ogni volta che è necessario verificare se un elemento specifico si trova in una sequenza oppure no, è meglio convertire la sequenza una volta in un dizionario o in un hashset. Qualunque cosa tu crei dipende dal fatto che hai solo bisogno della chiave o dell'elemento che ha la chiave. Crea il dizionario/hashset solo una volta e cerca molto velocemente l'oggetto con la chiave.

Nel nostro caso vogliamo solo una sequenza con i guidi usati, non importa in quale transazione viene utilizzata.

var usedGuids = transDetails.Select(transDetail => transDetail.TxnDetails).Distinct(); 
var hashedGuids = new HashSet(usedGuids); 

(ho fatto due dichiarazioni per renderlo più facile da capire che cosa è fatto)

Ora, ogni volta che ho un GUID posso controllare molto velocemente se viene utilizzato o meno:

bool guidIsUsed = usedGuids.Contains(myGuid); 

Quindi la sequenza di carte in carddetails con un GUID che non è in transDetails è:

var hashedGuids = new HashSet(transDetails.Select(transDetail => transDetail.TxnDetails).Distinct()); 
var requestedCards = cardDetails.Where(card => !hashedGuids.Contains(card.Guid)); 
0

Usando il metodo sintassi concatenata per LINQ:

List<Card> result = cardDetails.Where(
    card => !transDetails.Exists(
     tran => tran.TxnDetails == card.TransactionRef.Split('|')[1] 
)).ToList(); 

Che cosa c'è di sbagliato nella tua ricerca?

cardDetails = cardDetails.Where(x => transDetails.Any(y => x.TransactionRef.Contains(y.TxnDetails) == false)).ToList(); 

Questo è quello che hai scritto:

Trovami Tutte le carte che soddisfano questa condizione: C'è qualche transazione nella mia lista di operazioni che questa particolare transazione ha TxnDetails che non possono essere trovati in TxnDetails di questa particolare carta?

posso vedere problema qui:

Se una transazione ha un altro TxnId di una scheda (le probabilità sono piuttosto elevati), restituire questa scheda.

Quindi, in pratica si dovrebbe ottenere tutte le carte dalla query Se l'elenco delle transazioni ha almeno 2 diversi ID di transazione in esso