View Source Ndim (ndim v0.1.0)
The Ndim
lib provides functions for working with n-dimensional lists.
This module is particularly useful when you need to:
- Map functions over deeply nested lists at a specific level while maintaining their shape
- Transform between the n-dimensional list and the coordinated map
Terminology
N-dimensional List
An n-dimensional list is a regularly nested list structure where n represents the depth of nesting. Each level must maintain consistent structure:
1-dimensional list (vector)
[1, 2, 3]
2-dimensional list (matrix)
[
[1, 2, 3],
[4, 5, 6]
]
3-dimensional list (cube)
[
[
[1, 2], [3, 4]
],
[
[5, 6], [7, 8]
]
]
Coordinate map
A coordinate map is a map representation of an n-dimensional list where each value is keyed by its dimensional coordinates. This is useful for sparse data or when you need direct coordinate access:
From 2-dimensional list to 2d coordinate map
[
[1, 2], --> %{{0, 0} => 1, {0, 1} => 2,
[3, 4] {1, 0} => 3, {1, 1} => 4}
]
From 3-dimensional list to 3d coordinate map
[
[[1, 2]], --> %{{0, 0, 0} => 1, {0, 0, 1} => 2,
[[3, 4]] {1, 0, 0} => 3, {1, 0, 1} => 4}
]
Core functions
d2map/2
...d5map/2
- Map a function over a 2-dimensonal list (upto 5)dmap/3
- The general version which maps a function over elements at any specified dimensional listto_coordinate_map/1
- Transform a n-dimensional list to a coordinate map
Examples
Map a function over a 2-dimensional list
iex> numbers = [[1, 2], [3, 4]]
iex> Ndim.d2map(numbers, fn x -> x * 2 end)
[[2, 4], [6, 8]]
Transform a 2-dimensional list to a coordinate map
iex> vector = [[1, 2], [3, 4]]
iex> Ndim.to_coordinate_map(vector)
%{{0, 0} => 1, {0, 1} => 2, {1, 0} => 3, {1, 1} => 4}
Summary
Functions
Maps a function over a 2-dimensional list.
Takes a 2-dimensional list as the first argument, followed by a mapping function.
This is equivalent to calling dmap(list, 2, fun)
.
Maps a function over a 3-dimensional list.
Takes a 3-dimensional list as the first argument, followed by a mapping function.
This is equivalent to calling dmap(list, 3, fun)
.
Maps a function over a 4-dimensional list.
Takes a 4-dimensional list as the first argument, followed by a mapping function.
This is equivalent to calling dmap(list, 4, fun)
.
Maps a function over a 5-dimensional list.
Takes a 5-dimensional list as the first argument, followed by a mapping function.
This is equivalent to calling dmap(list, 5, fun)
.
Maps a function over a specified dimensional list. Takes a n-dimensional list as the first argument, followed by the nesting level, then a mapping function.
Gets the depth (dimension) of a regular nested list. Returns 0 for an empty list, 1 for a flat list, and n for an n-dimensional nested list. Only works with regular nested structures where elements at the same depth share the same dimensionality.
Converts an n-dimensional nested list into a coordinate map where each value is keyed by its dimensional coordinates. The dimension can be specified to handle specific cases, or automatically detected from the list structure.
Functions
Maps a function over a 2-dimensional list.
Takes a 2-dimensional list as the first argument, followed by a mapping function.
This is equivalent to calling dmap(list, 2, fun)
.
Examples
iex> numbers = [[1, 2], [3, 4], [5, 6]]
iex> Ndim.d2map(numbers, fn x -> x * 2 end)
[[2, 4], [6, 8], [10, 12]]
iex> strings = [["a", "b"], ["c", "d"]]
iex> Ndim.d2map(strings, &String.upcase/1)
[["A", "B"], ["C", "D"]]
Maps a function over a 3-dimensional list.
Takes a 3-dimensional list as the first argument, followed by a mapping function.
This is equivalent to calling dmap(list, 3, fun)
.
Examples
iex> numbers = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
iex> Ndim.d3map(numbers, fn x -> x + 1 end)
[[[2, 3], [4, 5]], [[6, 7], [8, 9]]]
iex> strings = [[["hello", "world"], ["foo"]], [["bar"], ["baz", "qux"]]]
iex> Ndim.d3map(strings, &String.capitalize/1)
[[["Hello", "World"], ["Foo"]], [["Bar"], ["Baz", "Qux"]]]
Maps a function over a 4-dimensional list.
Takes a 4-dimensional list as the first argument, followed by a mapping function.
This is equivalent to calling dmap(list, 4, fun)
.
Examples
iex> numbers = [[[[1, 2], [3]], [[4, 5]]], [[[6]], [[7, 8], [9]]]]
iex> Ndim.d4map(numbers, fn x -> x * 2 end)
[[[[2, 4], [6]], [[8, 10]]], [[[12]], [[14, 16], [18]]]]
iex> strings = [[[[{"a", 1}]]], [[[{"b", 2}]]]]
iex> Ndim.d4map(strings, fn {str, num} -> {String.upcase(str), num} end)
[[[[{"A", 1}]]], [[[{"B", 2}]]]]
Maps a function over a 5-dimensional list.
Takes a 5-dimensional list as the first argument, followed by a mapping function.
This is equivalent to calling dmap(list, 5, fun)
.
Examples
iex> numbers = [[[[[1]], [[2]]], [[[3]]]], [[[[4]]], [[[5]], [[6]]]]]
iex> Ndim.d5map(numbers, fn x -> x + 10 end)
[[[[[11]], [[12]]], [[[13]]]], [[[[14]]], [[[15]], [[16]]]]]
iex> data = [[[[[%{x: 1}]], [[%{x: 2}]]]], [[[[%{x: 3}]]]]]
iex> Ndim.d5map(data, fn map -> Map.update!(map, :x, & &1 * 2) end)
[[[[[%{x: 2}]], [[%{x: 4}]]]], [[[[%{x: 6}]]]]]
Maps a function over a specified dimensional list. Takes a n-dimensional list as the first argument, followed by the nesting level, then a mapping function.
Parameters
list
- A n-dimensional nested list to transformdim
- The dimension at which to apply the function (1-based indexing)fun
- The function to apply at the specified dimension
Examples
iex> numbers = [[1, 2], [3, 4], [5, 6]]
iex> Ndim.dmap(numbers, 2, fn i -> i * 10 end)
[[10, 20], [30, 40], [50, 60]]
iex> nested = [[["a", "b"], ["c", "d"]], [["e", "f"], ["g", "h"]], [["i", "j"], ["k", "l"]]]
iex> Ndim.dmap(nested, 3, &String.upcase/1)
[[["A", "B"], ["C", "D"]], [["E", "F"], ["G", "H"]], [["I", "J"], ["K", "L"]]]
Gets the depth (dimension) of a regular nested list. Returns 0 for an empty list, 1 for a flat list, and n for an n-dimensional nested list. Only works with regular nested structures where elements at the same depth share the same dimensionality.
Examples
iex> Ndim.get_depth([])
0
iex> Ndim.get_depth([1, 2, 3])
1
iex> Ndim.get_depth([[1, 2], [3, 4]])
2
iex> Ndim.get_depth([[[1, 2]], [[3, 4]]])
3
Error cases
iex> Ndim.get_depth([1, [2, 3]])
** (ArgumentError) list is not a regular nested structure
iex> Ndim.get_depth([[1, 2], [3]])
** (ArgumentError) list is not a regular nested structure
Return Value
Returns an integer representing the depth (dimension) of the nested list. For irregular structures, raises an ArgumentError.
Note: The depth counting starts from 0 for an empty list, 1 for a flat list, and increments by one for each level of regular nesting.
Converts an n-dimensional nested list into a coordinate map where each value is keyed by its dimensional coordinates. The dimension can be specified to handle specific cases, or automatically detected from the list structure.
Parameters
list
- An n-dimensional nested list to convertdimension
- (Optional) The expected dimension of the nested list
Examples
# Automatic dimension detection
iex> matrix = [[1, 2], [3, 4]]
iex> Ndim.to_coordinate_map(matrix)
%{{0, 0} => 1, {0, 1} => 2, {1, 0} => 3, {1, 1} => 4}
iex> cube = [[[1, 2]], [[3, 4]]]
iex> Ndim.to_coordinate_map(cube)
%{{0, 0, 0} => 1, {0, 0, 1} => 2, {1, 0, 0} => 3, {1, 0, 1} => 4}
# Explicitly specifying 2D
iex> matrix = [[1, 2], [3, 4]]
iex> Ndim.to_coordinate_map(matrix, 2)
%{{0, 0} => 1, {0, 1} => 2, {1, 0} => 3, {1, 1} => 4}
# Explicitly specifying 3D
iex> cube = [[[1, 2]], [[3, 4]]]
iex> Ndim.to_coordinate_map(cube, 3)
%{{0, 0, 0} => 1, {0, 0, 1} => 2, {1, 0, 0} => 3, {1, 0, 1} => 4}
Return Value
Returns a map where keys are tuples of coordinates and values are the corresponding elements from the input list.
Note: Coordinates use zero-based indexing, following programming conventions.