feat(sterni/nix/lists): implement transpose

This function is inspired by BQN's [⍉] though it is much less elegant
since Nix lacks multi-dimensional arrays. I thought this would be useful
to to avoid multiple `map`s over a single list if we want to return
multiple, separate values from it:

  transpose (builtins.map (x: [ (calcA x) (calcB x) ]) myList)

  # => [ [ (calcA a) … ] [ (calcB a) … ] ]

While this is quite elegant, it turns out that it is faster to write out
multiple maps:

  [ (builtins.map calcA myList) (builtins.map calcB myList) ]

[⍉]: https://mlochbaum.github.io/BQN/doc/transpose.html

Change-Id: Ic333c33af38ab03573b215c9696d75caf2ee18e7
Reviewed-on: https://cl.tvl.fyi/c/depot/+/11113
Reviewed-by: sterni <sternenseemann@systemli.org>
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
This commit is contained in:
sterni 2024-03-09 13:17:30 +01:00 committed by clbot
parent 37703c9e44
commit ad9b08e43d

View file

@ -0,0 +1,30 @@
{ ... }:
{
/* For a list of length n that consists of lists of length m,
return a list of length m containing lists of length n
so that
builtins.elemAt (builtins.elemAt orig a) b
== builtins.elemAt (builtins.elemAt transposed b) a
Essentially, if you think of the nested list as an array with two
dimensions, the two index axes are swapped.
The length of the inner lists m is determined based on the first element
and assumed to be used for all other lists. Malformed input data may
cause the function to crash or lose data.
Type: <n>[ <m>[ ] ] -> <m>[ <n>[ ] ]
*/
transpose = list:
let
innerLength = builtins.length (builtins.head list);
outerLength = builtins.length list;
in
builtins.genList
(inner: builtins.genList
(outer: builtins.elemAt (builtins.elemAt list outer) inner)
outerLength)
innerLength;
}