Reputation: 914
I have a number of lists - each instance of which contains 9 floating point numbers. What I effectively need to do is produce one new list that takes the first element from each of my lists and adds them together as my first element, then add the second element from each list as my second element, etc.
So effectively, if my data looks something like this:
List1 = [a1; b1; c1; d1; e1; f1; g1; h1; i1]
List2 = [a2; b2; c2; d2; e2; f2; g2; h2; i2]
...
Listn = [an; bn; cn; dn; en; fn; gn; hn; in]
Then I need to produce a new list Listx such that
Listx = [a1 + a2 + ... + an; b1 + b2 + ... + bn; ... ]
The number of lists I will be merging will vary (sometimes I may only have a single list of 9 numbers, and sometimes more than 100 lists, always 9 elements long), so I was wondering if anybody's got any advice on a nice idiomatic way of doing this?
I did have a look at this question and this one but both appear to advocate an intermediate step of indexing my elements first and then using groupby. This bugs me because a) I get the feeling there might be a more elegant solution for my particular case and b) performance may be an issue later - I don't want to optimize prematurely, but I also don't want to shoot myself in the foot.
Upvotes: 5
Views: 535
Reputation: 477
Here's an alternative one liner with default values when the list of lists is empty:
let sumVertically length = List.fold (List.map2 (+)) (List.replicate length 0)
//usage
//stolen from pad's answer
let listOfLists = List.init 1000 (fun i -> [i+1..i+9])
sumVertically 9 listOfLists
Upvotes: 0
Reputation: 41290
Here is a solution which works on a list of lists with the same length:
let mapN f = function
| [] -> []
| xss -> List.reduce (List.map2 f) xss
let inline merge xss = mapN (+) xss
// Usage
let yss = List.init 1000 (fun i -> [i+1..i+9])
let ys = merge yss
Upvotes: 7
Reputation: 92026
// Shamelessly stolen from:
// http://hackage.haskell.org/packages/archive/base/latest/doc/html/src/Data-List.html#transpose
let rec transpose = function
| [] -> []
| ([] :: xss) -> transpose xss
| ((x :: xs) :: xss) -> (x :: List.map List.head xss) :: transpose (xs :: List.map List.tail xss)
let fuse = transpose >> List.map List.sum
printfn "%A" (fuse [[3; 4]; [1; 90]; [34; 89]]) // prints [38; 183]
Upvotes: 1
Reputation: 48687
I'd go via something easier like a matrix:
let merge xss =
let m = matrix xss
List.map (m.Column >> Seq.sum) [0..m.NumCols-1]
Upvotes: 2
Reputation: 62975
Here's one approach:
let merge lists =
let rec impl acc lists =
match List.head lists with
| [] -> List.rev acc
| _ -> let acc' = (lists |> List.map List.head |> List.reduce (+))::acc
let lists' = List.map List.tail lists
impl acc' lists'
impl [] lists
Some notes:
List.reduce (+)
is used instead of List.sum
or List.sumBy
because the latter only work for numeric types whereas (+)
can work for e.g. string
.merge
is deduced to be of type int list list -> int list
rather than being generic due to subtleties of the way operator +
works. If you only need this to work for a single type, and that type is not int
(e.g. float
), then adding a type annotation to merge
will be sufficient:
let merge (lists:float list list) =
merge
can be marked inline
and then will work for any type that supports operator +
, but this will lead to lots of bloat in your IL if there are more than one or two call-sites. If you have multiple types that need to work with merge
, and all are known beforehand, then a good workaround here is to make merge
inline
(and possibly private
) then define different type-specific functions that are implemented in terms of the generic merge
:
let inline merge lists =
let rec impl acc lists =
match List.head lists with
| [] -> List.rev acc
| _ -> let acc' = (lists |> List.map List.head |> List.reduce (+))::acc
let lists' = List.map List.tail lists
impl acc' lists'
impl [] lists
let mergeInts (lists:int list list) = merge lists
let mergeFloats (lists:float list list) = merge lists
let mergeStrings (lists:string list list) = merge lists
If you then only call the type-specific merge
s, the IL bloat should be negligible.
Upvotes: 1