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.