2015-08-24 6 views
6

Ich habe zwei Objekte nämlich Karte und Transaktion:Filter zwei Listen auf eine Eigenschaft C# Linq

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

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

Hinweis: Die TransactionRef ist das Format Date|TxnID

Ich habe auch eine Liste der zwei Objekte List<Card> cardDetails und 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'} 

Ich möchte CardDetails mit TransDetails basierend auf TxnDetails filtern, so dass es die Elemente filtert, die die TxnDetails aus der 2. Liste nicht enthalten.

Dies sollte der Ausgang sein:

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

Ich habe wie diese mit Linq versucht:

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

aber es gibt immer die Liste als leer. Ich habe viele Varianten dieser Abfrage ohne Erfolg ausprobiert. Ich weiß, dass diese Frage vor und nach der Suche nach ihnen und dem Ausprobieren ihrer Lösungen gestellt wurde. Ich bin immer noch nicht in der Lage, es richtig zu machen.

Kann jemand vorschlagen, was mit meiner Frage falsch ist?

Hinweis: Eine Sache, die ich vergessen zu erwähnen, ist, dass diese Listen Tausende von Datensätzen enthalten können. Leistung ist also auch wichtig.

+0

Diese Abfrage sollte alle Karten tatsächlich zurückkehrt, sind Sie sicher, dass es keine gibt? –

+0

Ja, es gibt nichts zurück. Ich möchte nur den dritten Eintrag in den cardDetails zurückgeben, d. H. Die keine TxnDetails aus der zweiten Liste enthält. – nitinvertigo

+0

Warum 'TxnDetails.ToString()' wenn 'TxnDetails' eine Zeichenfolge ist? –

Antwort

5

Dies sollte es tun

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


Eine Möglichkeit, dies ein wenig alle möglichen Transaktionsdetails in einem Hash im Voraus festgelegt speichern würde zu optimieren sein. Das Nachschlagen sollte dann ziemlich nahe bei O (1) sein (unter der Annahme einer fairen Hashcode-Verteilung), anstelle von O (n) - was die Gesamtkomplexität des Algorithmus von O (n · k) auf O (n + k) reduziert.

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

Sie könnten 'EndsWith()' auch in dieser speziellen Situation verwenden –

+0

@CallumLinington Das ist ein guter Punkt – dcastro

1

Wie wäre das?

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

Full-Demo:

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

es ist nur eine Klammer Problem, soll die == false kommt nach )) nicht der ersten Schließung einer.

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

Ursache mit Ihrem tatsächlichen Code, tun Sie nur das Gegenteil von dem, was Sie wollen!

Sie können auch

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

oder jede improvment vorgeschlagen, aber der Code ist im Grunde ganz in der Nähe von richtig;)

+0

Ja, Sie haben Recht! :) Ich habe es geändert und es funktioniert. Danke – nitinvertigo

2

Diese Abfrage sollte es tun:

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

Aber Gibt es einen bestimmten Grund, warum Sie zwei Daten in einem Feld kombinieren? Ich empfehle, das Feld TransactionRef in Ihrer Card Klasse in zwei Felder zu zerlegen: TransactionDate und TransactionID, um die Manipulation von Strings in Abfragen zu vermeiden.

+1

Ja, das ist die einfachste Lösung. Eigentlich ist das Problem ** Any ** sollte sich in ** All ** ändern. – daniel

+0

@SaedAmini eigentlich hole ich alle Daten aus der azurblauen Datenbank und es kommt nur in diesem Format. Mein Hauptanliegen ist, wie wird die Leistung für etwa 100K Einträge in der Liste sein – nitinvertigo

0

Wenn Leistung wichtig ist, schlage ich vor, dass Sie der Klasse Card zuerst eine Eigenschaft geben, die den Teil nach dem '|' Charakter. Je nachdem, wie oft Sie diese Abfrage durchführen möchten, wie oft Sie eine Karte erstellen, könnte es sogar möglich sein, den Konstruktor den transactionRef in einen Teil vor dem '|' und ein Teil nach dem '|'.

Welche Methode Sie auch wählen, ist für die Abfrage nicht wichtig. Nehmen wir an, Klasse-Karte verfügt über eine Eigenschaft:

string Guid {get {return ...;} 

Ich verstehe, dass Sie eine Sequenz aller Karten aus der Sequenz cardDetails wollen, die keine Guid haben, die eine der TxnDetails der Transaktionen in der Folge von transDetails gleich .

Oder mit anderen Worten: Wenn Sie in TxnDetails eine Sequenz aller verwendeten Guids erstellen würden, möchten Sie alle Karten in CardDetails, die eine Guid haben, die nicht in der Reihenfolge aller verwendeten Guids ist.

Sie könnten Any() dafür verwenden, aber das würde bedeuten, dass Sie die TransDetails-Sequenz für jede Karte durchsuchen müssen, die Sie überprüfen möchten.

Immer wenn Sie überprüfen müssen, ob sich ein bestimmtes Objekt in einer Sequenz befindet oder nicht, ist es besser, die Sequenz einmal in ein Dictionary oder ein HashSet zu konvertieren. Was immer Sie erstellen, hängt davon ab, ob Sie nur den Schlüssel oder das Element mit dem Schlüssel benötigen. Erstellen Sie das Wörterbuch/hashset nur einmal und suchen Sie sehr schnell nach dem Element mit dem Schlüssel.

In unserem Fall wollen wir nur eine Sequenz mit verwendeten Guids, egal in welcher Transaction diese verwendet wird.

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

(Ich habe zwei Aussage, die es leichter zu verstehen, was geschehen ist)

Nun, wenn ich eine GUID kann ich sehr schnell überprüfen, ob es verwendet wird oder nicht:

bool guidIsUsed = usedGuids.Contains(myGuid); 

So Ihre Sequenz von Karten in cardDetails mit einer GUID, die nicht in transDetails ist:

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

Methode verwenden Chainlining-Syntax für LINQ:

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

Was ist falsch mit Ihrer Abfrage?

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

Dies ist, was Sie geschrieben haben:

alle Karten Finden Sie mir, die diese Bedingung erfüllen: Gibt es eine Transaktion in meiner Liste der Transaktionen, die diese bestimmte Transaktion TxnDetails hat, die in TxnDetails nicht gefunden werden kann von dieser bestimmten Karte?

I Problem hier sehen können:

Wenn eine Transaktion eine andere TxnId als eine Karte hat (die Chancen sind ziemlich hoch), bringe diese Karte.

Also, im Grunde sollten Sie alle Karten aus Ihrer Anfrage erhalten, wenn Ihre Transaktionsliste mindestens 2 verschiedenen Transaktions-IDs darin hat

Verwandte Themen