2010-09-23 21 views
5

Ho due sequenze (di tuple) su cui ho bisogno di fare un join:Come chiamo Enumerable.Join da F #?

  • Seq 1: [(City1 * Pin1), (City2 * Pin2), (City1 * Pin3), (City1 * pin4)]
  • Seq 2: [(Pin1 * ProductA), (Pin2 * ProductB), (Pin1 * ProductC), (Pin2 * ProductA)]

nella sequenza (di tuple):

  • [(City1 * ProdottoA), (Città2 * ProdottoB), (Città * ProdottoC), (City2 * Prodotto A) ...]

In C# ho potuto fare questo con il Linq Registrato come metodo di estensione:

seq1.Join(seq2, t => t.Item2, t=> t.Item1, 
    (t,u) => Tuple.Create(t.Item1, u.Item2)) 

Come faccio a ottenere questo risultato in F #? Non riesco a trovare un join su Seq lì.

sessione
+0

Sto cercando un'implementazione efficiente in quanto questo elenco di prodotti/pin è piuttosto lungo. Linq sembra funzionare bene per me, ma non riesce a farlo funzionare da f #. –

+0

Le sequenze F # implementano persino 'IEnumerable'? In caso contrario, non è possibile utilizzare LINQ. –

+0

Sì, implementano IEnumerable. –

risposta

6

Edit: In realtà, si può semplicemente utilizzare LINQ:

> open System.Linq;; 
> let ans = seq1.Join(seq2, (fun t -> snd t), (fun t -> fst t), (fun t u -> (fst t, snd u)));; 

perché non utilizzare s 'F # native Seq funzioni? Se vedi at the docs e at this question puoi semplicemente usare questi invece di LINQ. Prendere la funzione Seq.map2 ad esempio:

> let mapped = Seq.map2 (fun a b -> (fst a, snd b)) seq1 seq2;; 

val it : seq<string * string> = 
    seq [("city1", "product1"); ("city2", "product2")] 

dovrebbe darvi ciò che si vuole, dove e seq1seq2 sono le tue prime e seconde sequenze.

+0

Le città sarebbero una a molte con pin e pin sarebbero molti per molti con i prodotti. Potresti spiegare come funzionerebbe? –

+0

Vuoi dire che potresti avere '[(City1 * Pin1 * Pin2), (City2 * Pin2)]' e '[(Pin1 * ProdottoA), (Pin2 * ProdottoB * Prodottoc)] 'vale a dire usando tuple che più di 2 elementi? –

+0

No, voglio dire, potrei avere più elementi nella sequenza con la stessa città e un altro pin. Allo stesso modo, potrei avere più elementi con lo stesso pin e un prodotto diverso o viceversa nel seq 2. La tupla avrà sempre 2 elementi. –

2

F # Interactive:

> let seq1 = seq [("city1", "pin1"); ("city2", "pin2")];; 

val seq1 : seq<string * string> = [("city1", "pin1"); ("city2", "pin2")] 

> let seq2 = seq [("pin1", "product1"); ("pin2", "product2")];; 

val seq2 : seq<string * string> = [("pin1", "product1"); ("pin2", "product2")] 

> Seq.zip seq1 seq2;; 
val it : seq<(string * string) * (string * string)> = 
    seq 
    [(("city1", "pin1"), ("pin1", "product1")); 
    (("city2", "pin2"), ("pin2", "product2"))] 
> Seq.zip seq1 seq2 |> Seq.map (fun (x,y) -> (fst x, snd y));; 
val it : seq<string * string> = 
    seq [("city1", "product1"); ("city2", "product2")] 

Inoltre, è necessario essere in grado di utilizzare le query LINQ su sequenze, tanto per essere sicuri di avere un riferimento per l'assemblaggio System.Linq e ha aperto uno spazio dei nomi open System.Linq

UPDATE: in uno scenario complesso è possibile utilizzare le espressioni di sequenza come segue:

open System 

let seq1 = seq [("city1", "pin1"); ("city2", "pin2"); ("city1", "pin3"); ("city1", "pin4")] 
let seq2 = seq [("pin1", "product1"); ("pin2", "product2"); ("pin1", "product3"); ("pin2", "product1")] 

let joinSeq = seq { for x in seq1 do 
         for y in seq2 do 
          let city, pin = x 
          let pin1, product = y 
          if pin = pin1 then 
           yield(city, product) } 
for(x,y)in joinSeq do 
    printfn "%s: %s" x y 

Console.ReadKey() |> ignore 
+0

3 secondi prima di me! Non avrebbe dovuto aggiungere collegamenti ... –

+0

Le città avrebbero più di un pin e i pin sarebbero molti per molti con i prodotti. –

+0

Quindi, l'ultimo comando: creare un elenco di coppie di tuple come mostrato in precedenza (Seq.zip), quindi mappare questo elenco al nuovo, ottenendo il primo elemento della prima tupla (fst x) e il secondo elemento della seconda tupla (snd y). –

2

Penso che non sia esattamente chiaro quali risultati ci si aspetta, quindi le risposte sono un po 'confuse. Il tuo esempio potrebbe essere interpretato in due modi (come zippando o come unendo) e sono drammaticamente diversi.

  • Zipping: Se si dispone di due liste della stessa lunghezza e si desidera allineare correspoding articoli (ad esempio primo elemento dalla prima lista con 1 ° punto dalla seconda lista; 2 ° elemento dalla prima lista con 2 ° voce dal secondo elenco, ecc.), quindi esaminare le risposte che utilizzano List.zip o List.map2.

    Tuttavia, questo significherebbe che le liste sono ordinate per pin e pin sono unici.In tal caso non è necessario utilizzare Join e anche in C#/LINQ, è possibile utilizzare il metodo di estensione Zip.

  • Partecipare: Se le liste possono avere diverse lunghezze, pin non possono essere ordinati o non unico, allora avete bisogno di scrivere un vero e proprio join. Una versione semplificata del codice di Artem K sarebbe simile a questa:

    seq { for city, pin1 in seq1 do 
         for pin2, product in seq2 do 
          if pin1 = pin2 then yield city, product } 
    

    Questo può essere meno efficiente rispetto Join in LINQ, perché scorre tutti gli elementi nella seq2 per ogni elemento in seq1, quindi la complessità è O(seq1.Length * seq2.Length). Non sono sicuro, ma penso che il Join potrebbe utilizzare un po 'di hashing per essere più efficiente. Invece di utilizzare Join metodo direttamente, probabilmente definire un piccolo aiutante:

    open System.Linq 
    module Seq = 
        let join (seq1:seq<_>) seq2 k1 k2 = 
        seq1.Join(seq2, (fun t -> k1 t), (fun t -> k2 t), (fun t u -> t, u)) 
    

    Quindi è possibile scrivere qualcosa del genere:

    (seq1, seq2) 
        ||> Seq.join snd fst 
        |> Seq.map (fun (t, u) -> fst t, snd u) 
    

Infine, se si sa che c'è esattamente un città unica per ogni prodotto (le sequenze hanno la stessa lunghezza e pin sono unici in entrambe), quindi è possibile ordinare entrambe le sequenze per pin e quindi utilizzare zip - questo potrebbe essere più efficiente dell'uso di join (specialmente se si potesse mantenerela sequenza ordinata da alcune operazioni precedenti).

+0

Ciao, volevo un join (prodotto cross) e implementato usando un dizionario in LINQ che lo rende più veloce. –