Crystal does not have a built-in, high-level representation of multidimensional matrices like Pythonâs NumPy or Rubyâs NArray. However, suppose youâre thinking in terms of a Pandas DataFrame, an Excel sheet, or a 2D matrix.
a = [[1, 2], [3, 4]]
b = a.transpose
c = b.map(&.sort)
d0 = c[0]
d1 = c[1]
e = d0.zip(d1).map { |x, y| x + y }
If you want to sort by row (or column) and then aggregate by column (or row)âsimilar to operations in Pandasâthe simplest approach is to use transpose
twice:
crystal
e = a.transpose # First transpose: rows become columns
.map(&.sort) # Sort each column
.transpose # Second transpose: return to original row orientation
.map { |row| row.sum } # Sum each row
However, this method may be costly because it allocates memory for the entire matrix twice. Thatâs why you might prefer using zip
.
In such cases, reduce
can be an alternative:
crystal
a.transpose
.map(&.sort)
.reduce(Array.new(a.size, 0)) { |acc, col|
acc.zip(col).map { |x, y| x + y }
}
While reduce
may run just fine on my computer, my brain doesnât seem to come with enough RAM to reason clearly about such method chains. So I prefer a more explicit and split-up version:
sorted = a.transpose.map(&.sort)
col0 = sorted[0]
col1 = sorted[1]
result = col0.zip(col1).map { |x, y| x + y }
Even the Reduce version is not perfect in terms of efficiency.
- Reuse
Array.new(size, 0)
as a preallocated buffer instead of creating a new array each time.
- Avoid
zip
and instead use an indexed for loop with explicit i access.
I wrote this answer while asking ChatGPT, but ChatGPT also got it wrong twice, so I used my brain to correct it, but Iâm not sure if this is still completely correct. it seems AI is not good at reduce either.