0

I'm trying to iterate through an array, @chem_species = ["H2", "S", "O4"] and multiply a constant times the amount of constants present: H = 1.01 * 2, S = 32.1 * 1 and so on. The constants are of course defined within the class, before the instance method.

The code I've constructed to do this does not function:

def fw
x = @chem_species.map { |chem| chem.scan(/[A-Z]/)}
y = @chem_species.map { |chem| chem.scan({/\d+/)}
@mm = x[0] * y[0] 
end

yields -> TypeError: can't convert Array into Integer

Any suggestions on how to better code this? Thank you for your insight in advance.

5 Answers 5

3

How about doing it all in one scan & map? The String#scan method always returns an array of the strings it matched. Look at this:

irb> "H2".scan /[A-Z]+|\d+/i
  => ["H", "2"]

So just apply that to all of your @chem_species using map:

irb> @chem_species.map! { |chem| chem.scan /[A-Z]+|\d+/i }
  => [["H", "2"], ["S"], ["O", "4"]]

OK, now map over @chem_species, converting each element symbol to the value of its constant, and each coefficient to an integer:

irb> H = 1.01
irb> S = 32.01
irb> O = 15.99
irb> @chem_species.map { |(elem, coeff)| self.class.const_get(elem) * (coeff || 1).to_i }
  => [2.02, 32.01, 63.96]

There's your molar masses!

By the way, I suggest you look up the molar masses in a single hash constant instead of multiple constants for each element. Like this:

MASSES = { :H => 1.01, :S => 32.01, :O => 15.99 }

Then that last map would go like:

@chem_species.map { |(elem, coeff)| MASSES[elem.to_sym] * (coeff || 1).to_i }
Sign up to request clarification or add additional context in comments.

1 Comment

my god you're only 18. You should think about computational science in college. That was a great answer.
0

You have a syntax error in your code: Maybe it should be:

def fw
x = @chem_species.map { |chem| chem.scan(/[A-Z]/)}
y = @chem_species.map { |chem| chem.scan(/\d+/)}
@mm = x[0] * y[0] 
end

1 Comment

That was a translation error between my laptop and desktop. Thanks for catching that, I corrected it above however it is not the source of the error in ruby.
0

Have you looked at the output of @chem_species.map { |chem| chem.scan(/[A-Z]/)} (or the second one for that matter)? It's giving you an array of arrays, so if you really wanted to stick with this approach you'd have to do x[0][0].

Instead of mapping, do each

@chem_species.each { |c| c.scan(/[A-Z]/) }

Edit: just realized that that didn't work at all how I had thought it did, my apologies on a silly answer :P

Comments

0

Here's a way to multiply the values once you have them. The * operator won't work on arrays.

x = [ 4, 5, 6 ]
y = [ 7, 8, 9 ]
res = []
x.zip(y) { |a,b| res.push(a*b) }
res.inject(0) { |sum, v| sum += v}
# sum => 122

Or, cutting out the middle man:

x = [ 4, 5, 6 ]
y = [ 7, 8, 9 ]
res = 0
x.zip(y) { |a,b| res += (a*b) }
# res => 122

Comments

0

(one-liners alert, off-topic alert)

you can parse the formula directly:

"H2SO4".scan(/([A-Z][a-z]*)(\d*)/)
# -> [["H", "2"], ["S", ""], ["O", "4"]]

calculate partial sums:

aw = { 'H' => 1.01, 'S' => 32.07, 'O' => 16.00 }
"H2SO4".scan(/([A-Z][a-z]*)(\d*)/).collect{|e,x| aw[e] * (x==""?1:x).to_i}
# -> [2.02, 32.07, 64.0]

total sum:

"H2SO4".scan(/([A-Z][a-z]*)(\d*)/).collect{|e,x| aw[e] * (x==""?1:x).to_i}.inject{|s,x| s+x}
# -> 98.09

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.