Suppose two sorted lists with same length, key value list let k = [1;1;2;2;3;3]
, and a number listlet n = [1;2;3;4;5;6]
. The ith position on both lists maps with each other, meaning the element n=1
has a key value of k=1
, n=2
has a key value of k=1
... so on.
How to create a list that maps multiple elements with same key value in F#?
So the output would be in a format [[n1;n2;key];[n1;n2;key]...]
, the example output is [[1;2;1];[3;4;2];[5;6;3]]
.
CodePudding user response:
This can be achieved by "zipping" the two lists together and then grouping by the key:
let groupByKey keys numbers =
List.zip keys numbers
|> List.groupBy fst
|> List.map (fun (key, values) -> key, (values |> List.map snd))
groupByKey k n // [(1, [1; 2]); (2, [3; 4]); (3, [5; 6])]
The above version of the function doesn't have the exact output format that you specified, but it is a more structured output, where each item in the list is a tuple of the key and the list of values for that key. This might be preferred for cleaner code, depending on what you're going to do with the result.
To get your specified format it can be changed slightly:
let groupByKey keys numbers =
List.zip keys numbers
|> List.groupBy fst
|> List.map (fun (key, values) -> [
yield! values |> List.map snd
yield key
])
groupByKey k n // [[1; 2; 1]; [3; 4; 2]; [5; 6; 3]]
CodePudding user response:
I think what you are looking for is Seq.zip
, which takes two sequences (list are sequences) and gives back a sequence of pairs.
Also note you specified your lists with commas, but F# uses semi-colons to separate list elements.
let keys = [ 1; 1; 2; 2; 3; 3 ]
let values = [ 1; 2; 3; 4; 5; 6 ]
let map =
values
|> Seq.zip keys
|> Seq.groupBy (fun (k, v) -> k)
|> Seq.map (fun (k, vs) -> k, vs |> Seq.map snd |> Seq.toList)
|> Map.ofSeq
printfn "%A" map
// map [(1, [1; 2]); (2, [3; 4]); (3, [5; 6])]
To get the format you are looking for (although I think you should consider a Map
) you can unpack it like so:
let mapList =
map
|> Map.toSeq
|> Seq.map (fun (k, vs) -> vs @ [ k ])
|> Seq.toList
printfn "%A" mapList
// [[1; 2; 1]; [3; 4; 2]; [5; 6; 3]]
CodePudding user response:
What you describe is a map data-structure, so i would use one, instead of implementing it yourself with a bunch of list functions you can convert your two lists in such a way
let k = [1;1;2;2;3;3]
let n = [1;2;3;4;5;6]
let mapKeyToValues keys values =
let folder m k v =
m |> Map.change k (function
| Some old -> Some (v :: old)
| None -> Some [v]
)
List.fold2 folder Map.empty keys values
Now with
let r = mapKeyToValues k n
You get a data-structur like
Map [
1, [2; 1]
2, [4; 3]
3, [6; 5]
]
This would be equivalent to the JSON-Object
{
"1": [2,1],
"2": [4,3],
"3": [6,5]
}
Usually you could write
let r = Map (List.zip k n)
This way, out of a tuple with (k,v)
you create such a map-datastructure. But a new key, just ovverrides an older, so you get
Map [
(1, 2)
(2, 4)
(3, 6)
]
That's the reason why you need Map.change
. You go through every (key,value)
of a list with List.fold2
and then add it to the map data-structure. If a key is not present (the None
case) you create a list as a value with your only value. In the Some
case a key was already added, and you add your value to the list that is already present.
In this example i suppose the order of the values doesn't matter. If you want the same order as they appear in the value list, you must use List.foldBack2
instead of List.fold2
. But you must change the order of some arguments. This should be an exercise for you, to understand it better.
There is a Map
module with different functions to work with such a data-structure, and it also provides things like Map.map
, Map.filter
, Map.fold
, Map.find
, ... and so on. So use this instead. If really needed you also could use Map.toList
to transform it back to a list again, or use Map.fold
.
Anyway as a reminder, as some people use (List.zip
and then List.map
) or (List.zip
and then List.fold
). There is a List.map2
and List.fold2
that does this in one operation instead of two.