Deserializing an object with other objects in it - json

I'm making a two-player game of chess played on the command line. I have a class for every type of chess piece and a board class. The board class looks like this:
class Board
attr_accessor :board, :choice
def initialize
#board = Array.new(8){Array.new(8," ")}
#choice = choice
end
end
My other classes look like this:
class Bishop
attr_accessor :x_position, :y_position, :piece, :color, :counter, :moves
def initialize(position,boolean)
#x_position = position[0]
#y_position = position[1]
#piece = boolean ? "♝" : "♗"
#color = boolean ? "white" : "black"
#counter = 0
#moves = [[+1,-1],
[+1,+1],
[-1,+1],
[-1,-1]]
end
I added my pieces to the board like this:
#board[0][0] = Rook.new([0,0],false)
These are my methods to serialize and deserialize the data:
def to_json
JSON.generate({board: #board})
end
def save_game(string)
File.open("saved.json", "w") do |game_file|
game_file.write(string)
end
end
def load_game
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#board = data["board"]
end
After saving, the saved.json file looks like this:
{"board":[[" ","#<Knight:0x00000000e4fc28>","#<Bishop:0x00000000e4fa20>","#<Queen:0x00000000e4f890>","#<King:0x00000000e4f610>","#<Bishop:0x00000000e4f3e0>","#<Knight:0x00000000e4f278>","#<Rook:0x00000000e4e1c0>"],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],["#<Rook:0x00000000e4fd90>","#<Knight:0x00000000e4ed78>","#<Bishop:0x00000000e4eb70>","#<Queen:0x00000000e4ea08>","#<King:0x00000000e4e7b0>","#<Bishop:0x00000000e4e580>","#<Knight:0x00000000e4e3f0>"," "]]}
When I try to load back the data, the method that displays the board throws this error:
0 1 2 3 4 5 6 7
+----+----+----+----+----+----+----+----+
0|   /home/jacob/Desktop/chess/board.rb:30:in `block (2 levels) in display': undefined method `piece' for "#<Knight:0x00000000e4fc28>":String (NoMethodError)
from /home/jacob/Desktop/chess/board.rb:27:in `each'
from /home/jacob/Desktop/chess/board.rb:27:in `each_with_index'
from /home/jacob/Desktop/chess/board.rb:27:in `block in display'
from /home/jacob/Desktop/chess/board.rb:20:in `each'
from /home/jacob/Desktop/chess/board.rb:20:in `each_with_index'
It looks like my problem is that the objects come back as strings?
my display method:
def display
axis = 0
print " 0 1 2 3 4 5 6 7"
#board.each_with_index do |row,index|
print "\n"
#draw = " +----+----+----+----+----+----+----+----+"
puts #draw
print axis
axis +=1
if index.even?
row.each_with_index do|column,i|
if i.odd?
if column != " "
print "|"+" #{column.piece} ".bruno
else print "|"+" #{column} ".bruno
end
else
if column != " "
print "|"+" #{column.piece} "
else print "|"+" #{column} "
end
end
end
else
row.each_with_index do|column,j|
if j.even?
if column != " "
print "|"+" #{column.piece} ".bruno
else print "|"+" #{column} ".bruno
end
else
if column != " "
print "|"+" #{column.piece} "
else print "|"+" #{column} "
end
end
end
end
print "|"
end
print "\n"
print #draw
end

No, the problem is that your objects are saved as strings. Deserialization works fine, in this case. You'll have to explicitly specify what should be the json representation of your game pieces. Something like this:
class Rook
def to_json(*)
{ name: 'rook', position: 'A1', status: 'in_game' }.to_json
end
end
pieces = [Rook.new]
pieces.to_json # => "[{\"name\":\"rook\",\"position\":\"A1\",\"status\":\"in_game\"}]"
JSON.parse(pieces.to_json) # => [{"name"=>"rook", "position"=>"A1", "status"=>"in_game"}]
On deserialization you'll have to do the reverse. Construct proper game classes from plain ruby hashes, which you get from parsing JSON file.
Or if you don't actually care about JSON and just want to create some form of save file, then Marshal is your best friend. No need to override anything. Zero friction.
pieces = [Rook.new]
Marshal.dump(pieces) # => "\x04\b[\x06o:\tRook\x00" # write this to a file
# restore it later
Marshal.load("\x04\b[\x06o:\tRook\x00") # => [#<Rook:0x007fb50f825570>]

Related

Trying to serialize with JSON a save method in my Ruby Hangman game

I'm trying to get my save_game() method to work, nothing is getting written to the JSON file. This is my first assignment working with JSON and serialization in general. I'm not quite sure where I'm even going wrong.
These are my serialization methods:
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
This is my program, on line 92 I try to call my save_game method.
require 'json'
load 'display.rb'
class Hangman
attr_accessor :name
#name = name
def initialize
puts "What is your name?"
#name = gets.chomp
puts "
################################################
HANGMAN
################################################
_________
|
| |
| O
| /|\\
| |
| / \\
|
-----------------
Welcome #{#name} to Hangman. The computer will generate
a 5-12 letter random word. You will try to guess
that word one letter at a time. Try to solve the
puzzle before time runs out!
"
end
end
class Gameplay
attr_accessor :array, :filestuff, :random_word, :cipher, :random_word2, :counter
def initialize
#array = []
#filestuff = File.foreach('5text.txt') do |x|
chomped = x.chomp
#array << chomped if (chomped.length >= 5 and chomped.length <= 12)
end
#random_word = #array.sample
#cipher = #random_word.gsub(/[a-z]/, '*').split(//)
#random_word2 = #random_word.split(//)
#counter = 5
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
def choice(n)
#random_word2.each_with_index do |i,index|
if i == n
#cipher[index] = i
end
end
if n == #random_word2.join.to_s
puts "You win"
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
Hangman.new
else exit
end
end
if #random_word2.include?(n) == false
#counter -= 1
display
puts "#{#counter} guesses remaining."
puts "To save press 1"
save = gets.chomp
if save == "1"
#Will not save
save_game($b.to_json)
end
end
if #counter == 0
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
else exit
end
end
puts #cipher.join
end
#counter = 5
while #counter > 0
choice(gets.chomp)
end
end
end
Hangman.new
$b = Gameplay.new
You need to close the file in order to make sure your output is actually written to the disk ("flushed"). You can manually, call close:
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
or, you can use File.open, which takes a block and closes the file when the block ends:
File.open("saved.json", "w") do |game_file|
game_file.write(string)
end
Since, writing to the disk is a slow operation, Ruby (and all languages that I can think of right now) will hold off on actually writing the file until it has accumulated a certain amount of text in a buffer. Once it has reached this limit, it will flush the buffer and write everything in it to disk. In order to make sure all your text is actually written when trying to write a file, you need to call close on the file, and as part of closing it, Ruby will flush whatever is left in its buffer.
There are other ways of making sure your content is flushed but when you're just starting to learn about this stuff, it should suffice to just make sure to always close files when you're done reading or writing them.

Ruby Sequel MySQL Insert Error

I am learning Ruby and I am using the Sequel gem to handle my model. but I keep running into an error I can't seem to hunt down. The thing that doesn't make sense is that is it failing on an insert because a field doesn't exist in a table that I am inserting into. However, I am not attempting to use the field that doesn't exist.
Here is my error:
0
{:id=>5, :name=>"barcelona"} : {:id=>4, :name=>"berlin"}
/Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/adapters/mysql.rb:175:in `query': Mysql::Error: Unknown column 'name' in 'field list' (Sequel::DatabaseError)
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/adapters/mysql.rb:175:in `block in _execute'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/database/logging.rb:33:in `log_yield'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/adapters/mysql.rb:175:in `_execute'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/adapters/shared/mysql_prepared_statements.rb:34:in `block in execute'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/database/connecting.rb:250:in `block in synchronize'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/connection_pool/threaded.rb:98:in `hold'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/database/connecting.rb:250:in `synchronize'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/adapters/shared/mysql_prepared_statements.rb:34:in `execute'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/adapters/mysql.rb:160:in `execute_insert'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/dataset/actions.rb:927:in `execute_insert'
from /Users/username/.rvm/gems/ruby-2.2.0/gems/sequel-4.20.0/lib/sequel/dataset/actions.rb:336:in `insert'
from travellingAmerican.rb:70:in `addDist'
from travellingAmerican.rb:64:in `block (2 levels) in addLocations'
from travellingAmerican.rb:61:in `each'
from travellingAmerican.rb:61:in `block in addLocations'
from travellingAmerican.rb:58:in `each'
from travellingAmerican.rb:58:in `addLocations'
from travellingAmerican.rb:93:in `<main>'
Here is models.rb:
#Database
require 'sequel'
def create
DB.create_table(:locations) do
primary_key :id
String :name, :unique=>true
end
DB.create_table(:distances) do
primary_key :id
foreign_key :to, :locations
foreign_key :from, :locations
Float :miles, :default=>-1
TrueClass :valid, :default=>0
unique [:to, :from]
end
end
def drop
begin
DB.drop_table(:distances)
rescue
puts "Couldn't drop Distances"
end
begin
DB.drop_table(:locations)
rescue
puts "Couldn't drop locations"
end
end
DB = Sequel.connect(:adapter=>'mysql', :host=>'myHost', :user=>'myUser', :password=>'myPass', :database=>'myDB')
if __FILE__ == $PROGRAM_NAME
puts "Will Execute"
drop
create
puts "Done Executing"
end
Now the file the error is actually in, travellingAmerican.rb:
#Traveling American
require './models.rb'
require 'json'
def urlCreator(cities)
urls = []
rootUrl = "https://maps.googleapis.com/maps/api/distancematrix/json?"
origin = "origins="
dest = "destinations="
key = "myApiKey"
cities.each do |city|
city = city.gsub(/[" "]/, '+')
newOrigin = origin + city + "|"
newDest = dest
cities.each do |inCity|
newDest += inCity + "|"
end
newUrl = rootUrl + newOrigin + "&" + newDest + "&" + key
urls.push(newUrl)
end
urls
end
def processFile(file)
cities = []
File.readlines(file).each do |line|
line = line.strip
if line.count(",") == 1
line = line.split()
else
cities.push(line)
end
end
return cities
end
def addLocations(cities)
cities.each do |city|
begin
DB[:locations].insert(:name => city.downcase)
rescue Sequel::UniqueConstraintViolation
puts "Duplicate, not added"
end
end
allLocs = DB[:locations].where(:name => cities).order(:name).all
#if you puts allLocs it works beautifully
for i in 0...allLocs.length
puts i
toLoc = allLocs[i]
for j in (i+1)...allLocs.length
fromLoc = allLocs[j]
puts toLoc.to_s + " : " + fromLoc.to_s
#If you comment out the line below everything runs fine, minus the insertion obviously
addDist(toLoc, fromLoc)
end
end
end
def addDist(tos, froms)
#the line with the error
DB[:distances].insert(:to=>tos, :from=>froms)
end
if __FILE__ == $PROGRAM_NAME
popSize = -1
file = ""
count = 0
ARGV.each do|arg|
if count == 0
popSize = arg.to_i
else
file = arg
end
count += 1
end
if popSize == -1 or file == ""
abort("Usage: ruby travellingAmerican.rb popSize cityList")
end
cities = processFile(file)
addLocations(cities)
puts urlCreator(cities)
end
Lastly here is cities.csv if you would like to run this on your own machine:
San Diego
Munich
Paris
Berlin
Barcelona
Monte Carlo
Cinque Terre
Turin
Milan
Florence
Rome
Venice
To run this on your own machine just execute ruby travellingAmerican.rb 1000 cities.csv in a shell.
I hope I have provided enough info for everyone! Thanks for looking.
There are some problems I detected:
Here you add the cities with downcase:
cities.each do |city|
begin
DB[:locations].insert(:name => city.downcase)
rescue Sequel::UniqueConstraintViolation
puts "Duplicate, not added"
end
end
The you read them in a Hash:
allLocs = DB[:locations].where(:name => cities).order(:name).all
The condition is without the downcase! So you get no result.
You could do:
allLocs = DB[:locations].where(:name => cities.map(&:downcase)).order(:name).all
Later you use
addDist(toLoc, fromLoc)
toLoc and fromLoc are both Hashes, but the should be the key for the table cities.
This should work:
addDist(toLoc[:id], fromLoc[:id])
As an alternative you can adapt the insertion in addDist
DB[:distances].insert(:to=>tos[:id], :from=>froms[:id])
The usage of for is not ruby-like. I would use directly Sequel::Dataset#each:
selection = DB[:locations].where(:name => cities.map(&:downcase)).order(:name)
selection.each{|toLoc|
selection.each{|fromLoc|
puts toLoc.to_s + " : " + fromLoc.to_s
#If you comment out the line below everything runs fine, minus the insertion obviously
addDist(toLoc[:id], fromLoc[:id])
}
}
(You could discuss, if the where and order methods are really needed here)

rails ,paginate, sort and more than one table

I'm new to rails, I want to know how to paginate and sort by a particular column.
I can do this within one table, but when there is more than table, and the sorted column not in the main table.
I have tried many times but failed, and I also searched the web, but also in vain. Now I need your help...help please!
while paginate i'm using this:
# encoding: utf-8
module Pagination
class Paginator
attr_reader :item_count, :per_page, :page, :page_param
def initialize(*args)
if args.first.is_a?(ActionController::Base)
args.shift
ActiveSupport::Deprecation.warn "Paginator no longer takes a controller instance as the first argument. Remove it from #new arguments."
end
item_count, per_page, page, page_param = *args
#item_count = item_count
#per_page = per_page
page = (page || 1).to_i
if page < 1
page = 1
end
#page = page
#page_param = page_param || :page
end
def offset
(page - 1) * per_page
end
def first_page
if item_count > 0
1
end
end
def previous_page
if page > 1
page - 1
end
end
def next_page
if last_item < item_count
page + 1
end
end
def last_page
if item_count > 0
(item_count - 1) / per_page + 1
end
end
def first_item
item_count == 0 ? 0 : (offset + 1)
end
def last_item
l = first_item + per_page - 1
l > item_count ? item_count : l
end
def linked_pages
pages = []
if item_count > 0
pages += [first_page, page, last_page]
pages += ((page-2)..(page+2)).to_a.select {|p| p > first_page && p < last_page}
end
pages = pages.compact.uniq.sort
if pages.size > 1
pages
else
[]
end
end
def items_per_page
ActiveSupport::Deprecation.warn "Paginator#items_per_page will be removed. Use #per_page instead."
per_page
end
def current
ActiveSupport::Deprecation.warn "Paginator#current will be removed. Use .offset instead of .current.offset."
self
end
end
# Paginates the given scope or model. Returns a Paginator instance and
# the collection of objects for the current page.
#
# Options:
# :parameter name of the page parameter
#
# Examples:
# #user_pages, #users = paginate User.where(:status => 1)
#
def paginate(scope, options={})
options = options.dup
finder_options = options.extract!(
:conditions,
:order,
:joins,
:include,
:select
)
if scope.is_a?(Symbol) || finder_options.values.compact.any?
return deprecated_paginate(scope, finder_options, options)
end
paginator = paginator(scope.count, options)
collection = scope.limit(paginator.per_page).offset(paginator.offset).to_a
return paginator, collection
end
def deprecated_paginate(arg, finder_options, options={})
ActiveSupport::Deprecation.warn "#paginate with a Symbol and/or find options is depreceted and will be removed. Use a scope instead."
klass = arg.is_a?(Symbol) ? arg.to_s.classify.constantize : arg
scope = klass.scoped(finder_options)
paginate(scope, options)
end
def paginator(item_count, options={})
options.assert_valid_keys :parameter, :per_page
page_param = options[:parameter] || :page
page = (params[page_param] || 1).to_i
per_page = options[:per_page] || per_page_option
Paginator.new(item_count, per_page, page, page_param)
end
module Helper
include Redmine::I18n
# Renders the pagination links for the given paginator.
#
# Options:
# :per_page_links if set to false, the "Per page" links are not rendered
#
def pagination_links_full(*args)
pagination_links_each(*args) do |text, parameters, options|
if block_given?
yield text, parameters, options
else
link_to text, params.merge(parameters), options
end
end
end
# Yields the given block with the text and parameters
# for each pagination link and returns a string that represents the links
def pagination_links_each(paginator, count=nil, options={}, &block)
options.assert_valid_keys :per_page_links
per_page_links = options.delete(:per_page_links)
per_page_links = false if count.nil?
page_param = paginator.page_param
html = ''
if paginator.previous_page
# \xc2\xab(utf-8) = «
text = "\xc2\xab " + l(:label_previous)
html << yield(text, {page_param => paginator.previous_page}, :class => 'previous') + ' '
end
previous = nil
paginator.linked_pages.each do |page|
if previous && previous != page - 1
html << content_tag('span', '...', :class => 'spacer') + ' '
end
if page == paginator.page
html << content_tag('span', page.to_s, :class => 'current page')
else
html << yield(page.to_s, {page_param => page}, :class => 'page')
end
html << ' '
previous = page
end
if paginator.next_page
# \xc2\xbb(utf-8) = »
text = l(:label_next) + " \xc2\xbb"
html << yield(text, {page_param => paginator.next_page}, :class => 'next') + ' '
end
html << content_tag('span', "(#{paginator.first_item}-#{paginator.last_item}/#{paginator.item_count})", :class => 'items') + ' '
if per_page_links != false && links = per_page_links(paginator, &block)
html << content_tag('span', links.to_s, :class => 'per-page')
end
html.html_safe
end
# Renders the "Per page" links.
def per_page_links(paginator, &block)
values = per_page_options(paginator.per_page, paginator.item_count)
if values.any?
links = values.collect do |n|
if n == paginator.per_page
content_tag('span', n.to_s)
else
yield(n, :per_page => n, paginator.page_param => nil)
end
end
l(:label_display_per_page, links.join(', ')).html_safe
end
end
def per_page_options(selected=nil, item_count=nil)
options = Setting.per_page_options_array
if item_count && options.any?
if item_count > options.first
max = options.detect {|value| value >= item_count} || item_count
else
max = item_count
end
options = options.select {|value| value <= max || value == selected}
end
if options.empty? || (options.size == 1 && options.first == selected)
[]
else
options
end
end
end
end
end
I think the Kaminari gem is a little bit better now than will_paginate. Take a look at that one too.
Haven't you tried 'will_paginate' gem? It seems clearer for me than whatever in your code, and it's well-documented.
Here it is, take a look: https://github.com/mislav/will_paginate
If you want to paginate elements by quantity, like "100 posts per page", will_paginate is the best solution, I think.
But the problem appears, if you try to paginate elements by date or so on, when you can't predict, how many elements will be on the page. In this case, I'm using this solution will_paginate can it order by day. Works well for me.

Counting tag and text characters in an HTML document in Ruby

I need to parse an HTML document to count the number of characters for both tags (including attributes) and text in Ruby. For performance reasons, I don't want to use a DOM parser. I've looked at Nokogiri's SAX and Reader parsers and also to SaxMachine, but neither seems to offer me a way to track the parser's position in the input HTML.
Does anyone know a way to access this information in Ruby? Thanks in advance
Input string
html = <<-HTML
<html>
<head>
<title>Title</title>
</head>
<body>
Hello world!
</body>
</html>
HTML
Dumb solution
Crude solution, it counts every alphabet character (ie. </html> count for 4 characters).
tag_count = 0
text_count = 0
in_tag = false
html.each_char do |char|
case char
when '<'
in_tag = true
when '>'
in_tag = false
when /\w/
in_tag ? tag_count += 1 : text_count += 1
end
end
puts "Text char count: #{text_count}"
puts "Tag char count: #{tag_count}"
Nokogiri SAX solution
This one could be easily translated to another language (eg. Java).
require 'nokogiri'
class HtmlCounter < Nokogiri::XML::SAX::Document
attr_accessor :tag_count, :text_count, :comment_count
def initialize(filtered_tags = [])
#filtered_tags = filtered_tags
end
def start_document
#tag_count = Hash.new(0)
#text_count = Hash.new(0)
#comment_count = 0
#current_tags = []
end
def start_element(name, attrs)
# Keep track of the nesting
#current_tags.push(name)
if should_count?
# Count the end element as well
count_tag(name.length * 2)
count_tag(attrs.flatten.map(&:length).inject(0) {|sum, length| sum + length})
end
end
def end_element(name)
#current_tags.pop
end
def comment(string)
count_comment(string.length) if should_count?
end
def characters(string)
count_text(string.strip.length) if should_count?
end
def should_count?
# Are we in a filtered tag ?
(#current_tags & #filtered_tags).empty?
end
def count_text(count)
#text_count[#current_tags.last] += count
end
def count_tag(count)
#tag_count[#current_tags.last] += count
end
def count_comment(count)
#comment_count[#current_tags.last] += count
end
end
# Don't count things in title tags
counter = HtmlCounter.new(["title"])
parser = Nokogiri::HTML::SAX::Parser.new(counter)
parser.parse(html)
puts "Text char count: #{counter.text_count}"
puts "Tag char count: #{counter.tag_count}"
output :
Text char count: {"body"=>12}
Tag char count: {"html"=>8, "head"=>8, "body"=>8}
Hope this helps.

Lua: How to execute different blocks depending on conditions?

I have this table:
no_table ={
{a="3", b="22", c="18", d="ABC"},
{a="4", b="12", c="25", d="ABC"},
{a="5", b="15", c="16", d="CDE"},
}
This function:
function testfoo()
i = 1
while no_table[i] ~= nil do
foo(no_table[i])
i = i + 1
end
end
and the foo function:
function foo(a,b,c,d)
if no_table[i][4] ~= no_table[i-1][4]
then
print (a+b)
elseif no_table[i][4] == no_table[i-1][4]
then
print (b+c)
end
end
Can you help me find? :
A way to be able to check if the two tables are or not equal (currently it gives me cannot index nil)
A way to execute only the "print (b+c)" code if the equality is true, or if is not true then both "print (a+b)" first and "print (b+c) secondly without duplicating the code.
Lots of problems I'm seeing in this. First, I'd never rely on i being set in an external function, it really should be a local variable and passed as a parameter if you need it. That said, you need to check if no_table[x] exists before trying to access no_table[x][y]. So, for foo you'd have:
function foo(a,b,c,d)
if not (no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4])
then
print (a+b)
elseif no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4]
then
print (b+c)
end
end
Also, for numbers in the table, if you want to do arithmetic, you need to remove the quotes:
no_table ={
{a=3, b=22, c=18, d="ABC"},
{a=4, b=12, c=25, d="ABC"},
{a=5, b=15, c=16, d="CDE"},
}
Next, in testfoo, you're passing a table, so you either need to split out the values of a, b, c, and d on your function call, or you can just pass the table itself and handle that in foo:
function foo(t)
if not (no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4])
then
print (t.a+t.b)
elseif no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4]
then
print (t.b+t.c)
end
end
This results in:
> testfoo()
25
37
31
Edit: One final cleanup, since the conditions are the same, you can use an else rather than an elseif:
function foo(t)
if no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4]
then
print (t.b+t.c)
else
print (t.a+t.b)
end
end