2010-09-23 15 views
5

Tengo dos secuencias (de tuplas) en la que tengo que hacer una combinación:¿Cómo puedo llamar a Enumerable.Join desde F #?

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

en la secuencia (de tuplas):

  • [(City1 * ProductA), (City2 * ProductB), (Ciudad * ProductC), (City2 * Producto A) ...]

En C# que podría hacer esto utilizando el método de extensión LINQ Ingreso como:

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

¿Cómo lograr esto en C#? No puedo encontrar unirme a Seq allí.

sesión
+0

Estoy buscando una implementación eficiente ya que esta lista de Productos/Pin es bastante larga. Linq parece funcionar bien para mí, pero no puede hacer que funcione desde f #. –

+0

¿Las secuencias de F # incluso implementan 'IEnumerable'? Si no lo hacen, no puedes usar LINQ. –

+0

Sí, implementan IEnumerable. –

Respuesta

6

Editar: En realidad, sólo se puede utilizar LINQ:

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

por qué no utilizar C# 's nativos Seq funciones? Si mira at the docs y at this question, puede simplemente usar estos en lugar de LINQ. Tome la función Seq.map2 por ejemplo:

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

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

deben darle lo que quiere, donde seq1 y seq2 son su primera y segunda secuencias.

+0

Las ciudades serían una para muchos con alfileres y alfileres serían muchos para muchos con los productos. ¿Podrías explicarme cómo funcionaría? –

+0

¿Quiere decir que podría tener '[(City1 * Pin1 * Pin2), (City2 * Pin2)]' y '[(Pin1 * ProductA), (Pin2 * ProductB * Productc)]' es decir, utilizando tuplas que más de 2 ¿elementos? –

+0

No, quiero decir que podría tener varios elementos en la secuencia con la misma ciudad y un pin diferente. De manera similar, podría tener varios elementos con el mismo marcador y un producto diferente, o viceversa en la sección 2. Sin embargo, la tupla siempre tendrá 2 elementos. –

2

F # interactiva:

> 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")] 

Además, debe ser capaz de utilizar las consultas LINQ en las secuencias, sólo asegúrese de que tiene una referencia al ensamblado System.Linq y abrió un espacio de nombres open System.Linq

ACTUALIZACIÓN: en un escenario complejo que puede utilizar expresiones de secuencia de la siguiente manera:

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 segundos antes que yo! No debería haber agregado enlaces ... –

+0

Las ciudades tendrían más de un pin y los pins serían muchos para muchos con productos. –

+0

Entonces, lo que hace el último comando: hacer una lista de pares de tuplas como se muestra en el comando anterior (Seq.zip), luego asignar esta lista al nuevo, obtener el primer elemento de la primera tupla (fst x) y el segundo elemento de segunda tupla (snd y). –

2

Creo que no está exactamente claro qué resultados espera, por lo que las respuestas son un poco confusas. Su ejemplo podría interpretarse de dos maneras (ya sea como comprimiendo o como uniendo) y son radicalmente diferentes.

  • Zipping: Si tiene dos listas de la misma longitud y que desea alinear correspoding artículos (por ejemplo 1r artículo de primera lista con el 1er elemento de la segunda lista; segundo elemento de la primera lista con 2ª elemento de la segunda lista, etc.), luego mira las respuestas que usan List.zip o List.map2.

    Sin embargo, esto significa que las listas están ordenadas por pines y pines son únicos.En ese caso, no necesita usar Join e incluso en C#/LINQ, puede usar el método de extensión Zip.

  • Unir: Si las listas pueden tener longitudes diferentes, los pines pueden no estar ordenados o no ser únicos, entonces debe escribir una unión real. Una versión simplificada del código de Artem K podría tener este aspecto:

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

    esto puede ser menos eficiente que Join en LINQ, ya que recorre todos los artículos en seq2 para cada artículo en seq1, por lo que la complejidad es O(seq1.Length * seq2.Length). No estoy seguro, pero creo que Join podría usar algo hash para ser más eficiente. En lugar de utilizar Join método directamente, probablemente definir un pequeño ayudante:

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

    Entonces se puede escribir algo como esto:

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

Por último, si se sabe que hay exactamente una ciudad única para cada producto (las secuencias tienen la misma longitud y los pines son únicos en ambos), entonces puedes ordenar ambas secuencias por alfileres y luego usar zip - esto puede ser más eficiente que usar join (especialmente si pudieras mantener la secuencia ordenada de algunas operaciones anteriores).

+0

Hola, quería unirme (producto cruzado) y se implementó usando un diccionario en LINQ que lo hace más rápido. –