# Natrix - The Named Matrix

**URL:** <https://www.ruby-forum.com/t/natrix-the-named-matrix/174131>\
**Category:** Ruby\
**Created:** [August 21, 2009, 5:37am UTC](https://www.ruby-forum.com/t/natrix-the-named-matrix/174131 "2009-08-21T05:37:18Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![dbf](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/7feea3/32.png) [@dbf](https://www.ruby-forum.com/u/dbf)\
**Post date:** [August 21, 2009, 5:37am UTC](https://www.ruby-forum.com/t/natrix-the-named-matrix/174131/1 "2009-08-21T05:37:18Z")

</div>

Natrix is a class for making named matrix structures (more for text  
than math). It was designed to take rows of data and put the value of  
each row in a cell in the matrix. In other words, the row contains an  
x, y, and a value. It also supports very basic import and export of  
delimited text and output of JSON (array of arrays) and (array of  
hashes). It has a strict mode so it can raise an error if you try to  
set a cell that does not exist. Feedback is welcome.

# Natrix is a named matrix

# t = Natrix.new(

# :xs =\> %w(a b c d),

# :ys =\> %w(m n o p)

# )

# t.set(‘a’,‘p’,20)

# t.get(‘a’,‘p’) =\> 20

# t.get(0,3) =\> 20

class Natrix  
include Enumerable

# X Axis

attr\_accessor :xs

# Y Axis

attr\_accessor :ys

# Data

attr\_accessor :d

# Mode (open|strict)

# Strict ensures that you can not set() or get() if the keys do not

exist.  
attr\_accessor :mode

def initialize(a={},&b)  
@mode = ‘open’  
@d = Hash.new  
@xs = Array.new  
@ys = Array.new  
a.each{|k,v| send("#{k}=",v)}  
yield self if b  
end

# Make the keys strings

def xs=(\*args)  
@xs = args.flatten.map{|a| a.to\_s}  
end

def ys=(\*args)  
@ys = args.flatten.map{|a| a.to\_s}  
end

# The x key

def xk(key)  
return key.to\_s if String === key or Symbol === key  
xs[key] # integer index  
end

# The y key

def yk(key)  
return key.to\_s if String === key or Symbol === key  
ys[key] # integer index  
end

def validate\_input(x,y)  
# Validate input if strict mode  
if mode.to\_s == ‘strict’  
raise ArgumentError, “No X axis with a value of #{x.inspect}”  
unless  
xs.include?(xk(x))  
raise ArgumentError, “No Y axis with a value of #{y.inspect}”  
unless  
ys.include?(yk(y))  
end  
end

# Set a value

def set(x,y,val)  
# Validate input if strict mode  
if mode.to\_s == ‘strict’  
validate\_input(x,y)  
end  
d[xk(x)] ||= Hash.new  
d[xk(x)][yk(y)] = val  
end

# Get a value

def get(x,y)  
# Validate input if strict mode  
if mode.to\_s == ‘strict’  
validate\_input(x,y)  
end  
d[xk(x)][yk(y)] rescue nil  
end

# Each returns all the values x0y0, x1y0, x2y0, x0y1, x1y1, x2y1…

def each(&b)  
ys.each{|y|  
xs.each{|x|  
begin  
yield d[x][y]  
rescue  
yield nil  
end  
}  
}  
end

# Each X yields an array of values for an x column

def each\_x(x,&b)  
ys.each{|y|  
begin  
yield d[xk(x)][y]  
rescue  
yield nil  
end  
}  
end

# Each Y yields all the values for a y row

def each\_y(y,&b)  
xs.each{|x|  
begin  
yield d[x][yk(y)]  
rescue  
yield nil  
end  
}  
end

# Output delimted text

def to\_delimited(x\_delimiter="\t",y\_delimiter="\n")  
ys.map{|y|  
xs.map{|x|  
d[x][y] rescue nil  
}.join(x\_delimiter)  
}.join(y\_delimiter)  
end

# Build data from delimited text

def from\_delimited(data,x\_delimiter="\t",y\_delimiter="\n")  
data.split(y\_delimiter).each\_with\_index{|line,y|  
line.split(x\_delimiter).each\_with\_index{|cell,x|  
begin  
d[xs[x]][ys[y]] = cell  
rescue  
d[xs[x]] ||= Hash.new  
d[xs[x]][ys[y]] = cell  
end  
}  
}  
end

# Output a json array of hashes (You need to require json for this

to work)  
def to\_json\_ah  
“[#{ys.map{|y|  
“{#{xs.map{|x|  
“#{x.to\_json}: #{(d[x][y] rescue nil).to\_json}”  
}.join(”,”)}}"  
}.join(",")}]"  
end

# Output a json array of arrays (You need to require json for this

to work)  
def to\_json\_aa  
“[#{ys.map{|y|  
“[#{xs.map{|x|  
(d[x][y] rescue nil).to\_json  
}.join(”,”)}]"  
}.join(",")}]"  
end

end

---

<div class="post-metadata">

**Author:** ![dbf](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/7feea3/32.png) [@dbf](https://www.ruby-forum.com/u/dbf)\
**Post date:** [August 22, 2009, 12:11am UTC](https://www.ruby-forum.com/t/natrix-the-named-matrix/174131/2 "2009-08-22T00:11:22Z")

</div>

One thought. Instead of #get and #set you could use #[] and #[]=.
