Another CodeWars challenge I tackled in Ruby. The instructions were to take *multilinear non-constant polynomials *(that is, those with out degrees but with coefficients), and to simplify them. Such a problem would be:

-x – xy – yx +x -10z -10b

Should result in -10b – 10z + 2xy (as the x’s have cancelled each other, and the xy and yx variables are really the same). That’s not a bad example, because the instructions specify that the terms must be ordered first by the length of the variable set (the xy’s with their length of two letter should come after those with a single letter, like -10b and -10z). However, the ordering of the variables must be alphabetical. -xy and -yx combine to -2xy, because x comes before y. These were the instructions, and they were accomplished by the following code:

def simplify (poly)
answer = []
done = []
hash_done, terms = Hash.new(0)
poly.scan(/[\+\-]*[[:alnum:]]+/).each {|terms| terms; done << terms}
done.each do |term|
coefficient, charset,blank = term.partition(/[[:alpha:]]+/)
charset = charset.chars.sort!.join
coefficient = coefficient.concat "1" if /\A[-?\+?]\Z/.match coefficient or coefficient.empty?
hash_done[charset]=hash_done[charset] += coefficient.to_i
end
hash_done.reject! {|k,v|v==0}
done = hash_done.to_a
done = done.sort_by { |l| [l[0].length, l]}
done = done.each {|el| el[1] = el[1].to_s.tr!("1","") if el[1].abs == 1 ; el.reverse!; answer<<el.join}
answer=answer.join("+").gsub(/\+-/,"-")
end

Here’s the fully annotated code that explains each step in greater detail:

def simplify (poly)
answer = []
done = []
hash_done, terms = Hash.new(0)
#The following line scans the incoming string and, unless it is has a coefficient of 0,
#parses it by sign & coefficient & terms, and places it into an array named "done",
#the coefficient and sign both being optional.
#Terms with coefficient 0 aren't even placed into the array.
poly.scan(/[\+\-]*[[:alnum:]]+/).each {|terms| terms; done<<terms}
done.each do |term|
#break up the term into a coefficient and charset. Partitioning creates a third value which is of no use here
coefficient, charset,blank = term.partition(/[[:alpha:]]+/)
charset = charset.chars.sort!.join #sort newly created charset for final answer, and so no dupes appear in hash for same terms
#if the coefficient is simply +, -, or blank, this means the coefficient is technically 1. We'll add it now, and remove later
coefficient = coefficient.concat "1" if /\A[-?\+?]\Z/.match coefficient or coefficient.empty?
hash_done[charset]=hash_done[charset] += coefficient.to_i#this is the math that adss/subtracts the coefficient and stores in hash
end
hash_done.reject! {|k,v|v==0} #Remove all k/v pairs with a value of 0. They won't be part of the final answer
done = hash_done.to_a #the rest of the transformations will be manipulated within an array
done = done.sort_by { |l| [l[0].length, l]} #the assignment requires that the terms be sorted by length of charset first, then aplha
#Get rid of any multipliers of 1 by matching on the abs value of 1. If exists, strip the 1. Then flip the [char, digit] pair and join
done = done.each {|el| el[1] = el[1].to_s.tr!("1","") if el[1].abs == 1 ; el.reverse!; answer<<el.join}
#join every element in the array together into a string, but any +- combos that result should be simplified to just "-". Leading
#subtraction symbols will be left intact
answer=answer.join("+").gsub(/\+-/,"-")
end