2010-09-23 3 views
5

J'ai deux séquences (de tuples) sur lequel je dois faire une jointure:Comment puis-je appeler Enumerable.Join à partir de F #?

  • Seq 1: [(City1 * Pin1), (City2 * Broche2), (City1 * Pin3), (City1 * Pin4)]
  • Seq 2: [(Pin1 * ProduitA), (Pin2 * ProductB), (Pin1 * ProductC), (Pin2 * ProduitA)]

dans la séquence (de tuples):

  • [(City1 * ProduitA), (City2 * ProduitB), (Ville * ProductC), (City2 * Un produit) ...]

En C# je pourrais le faire en utilisant la méthode join Linq d'extension comme:

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

Comment puis-je y arriver en F #? Je ne peux pas trouver rejoindre Seq là-bas.

+0

Je suis à la recherche d'une implémentation efficace car cette liste de produits/broches est assez longue. Linq semble bien fonctionner pour moi, mais je n'arrive pas à le faire fonctionner avec f #. –

+0

Est-ce que les séquences F # implémentent même 'IEnumerable'? Si ce n'est pas le cas, vous ne pouvez pas utiliser LINQ. –

+0

Oui, ils implémentent IEnumerable. –

Répondre

6

Edit: En fait, vous pouvez simplement utiliser LINQ:

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

Pourquoi ne pas utiliser les fonctions natives Seq de F #? Si vous regardez at the docs et at this question vous pouvez simplement utiliser ceux-ci au lieu de LINQ. Prenez la fonction Seq.map2 par exemple:

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

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

devrait vous donner ce que vous voulez, où seq1 et seq2 sont vos première et deuxième séquences.

+0

Villes serait un à plusieurs avec épingle et épingles serait beaucoup à plusieurs avec des produits. Pourriez-vous expliquer comment cela fonctionnerait? –

+0

Voulez-vous dire que vous pourriez avoir '[(City1 * Pin1 * Pin2), (City2 * Pin2)]' et '[(Pin1 * ProductA), (Pin2 * ProductB * Productc)]' ie utilisant des tuples dont plus de 2 éléments? –

+0

Non, je veux dire que je pourrais avoir plusieurs éléments dans la séquence avec la même ville et la même broche. De même, je pourrais avoir plusieurs éléments avec la même broche et le produit différent ou vice versa dans le seq 2. Le tuple aura toujours 2 éléments cependant. –

2

F # Session 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")] 

En outre, vous devez être en mesure d'utiliser des requêtes LINQ sur des séquences, juste être sûr que vous avez une référence à l'assemblée System.Linq et a ouvert un espace de noms open System.Linq

MISE à JOUR: dans un scénario complexe, vous pouvez utiliser des expressions de séquence comme suit:

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 secondes avant moi! N'aurait pas dû ajouter de liens ... –

+0

Les villes auraient plus d'une épingle et les épingles seraient beaucoup à plusieurs avec des produits. –

+0

Donc, ce que fait la dernière commande: faire une liste de paires de tuples comme commande précédente montrée (Seq.zip), puis mapper cette liste sur la nouvelle, en obtenant le premier élément du premier tuple (fst x) et le second élément de deuxième tuple (snd y). –

2

Je pense que les résultats que vous attendez ne sont pas très clairs, donc les réponses sont un peu confuses. Votre exemple pourrait être interprété de deux façons (soit comme zipping ou comme joindre) et ils sont radicalement différents.

  • Zipping: Si vous avez deux listes de la même longueur et que vous voulez aligner les éléments correspoding (par ex 1er élément de la première liste avec le 1er élément de la deuxième liste, 2e élément de la première liste avec 2 élément de la deuxième liste, etc ..), puis regardez les réponses qui utilisent soit List.zip ou List.map2. Toutefois, cela signifie que les listes sont triées par broches et que les broches sont uniques.Dans ce cas, vous n'avez pas besoin d'utiliser Join et même en C#/LINQ, vous pouvez utiliser la méthode d'extension Zip. Si les listes peuvent avoir des longueurs différentes, les broches ne peuvent pas être triées ou non uniques, alors vous devez écrire une vraie jointure. Une version simplifiée du code par Artem K ressemblerait à ceci:

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

    Cela peut être moins efficace que Join dans LINQ, car il boucle à travers tous les éléments seq2 pour chaque élément de seq1, de sorte que la complexité est O(seq1.Length * seq2.Length). Je ne suis pas sûr, mais je pense que Join pourrait utiliser un peu de hachage pour être plus efficace. Au lieu d'utiliser Join méthode directement, je définirais probablement une petite aide:

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

    Ensuite, vous pouvez écrire quelque chose comme ceci:

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

Enfin, si vous savez qu'il ya exactement un ville unique pour chaque produit (les séquences ont la même longueur et les broches sont uniques dans les deux), alors vous pouvez simplement trier les deux séquences par broches et ensuite utiliser zip - cela peut être plus efficace que d'utiliser join (surtout si vous pouviez la séquence triée à partir de certaines opérations antérieures).

+0

Salut, je voulais une jointure (produit croisé) et son implémentation en utilisant un dictionnaire dans LINQ qui le rend plus rapide. –

Questions connexes