Ruby: Parsing a complex hash - ruby

I have a hash that goes multiple levels deep: http://gist.github.com/285350
I am trying to loop through each serving but I keep running into multiple nil[] errors even though the hash isn't nil.
For example:
food_hash["food"]["servings"]
Returns nil.[]
It might be because im half asleep but I can't seem to get down to the "serving_description"... could anyone help put me in the right direction?

I can't see get down to the "serving_description"
Note that (1) servings.serving is an array, and (2) food is not a key in the hash. Try this instead:
f["servings"]["serving"][0]["serving_description"]
=> "1 thin slice (yield after cooking)"
where f is the hash.

Is this what you're trying to do?
food_hash["servings"]["serving"][0]["serving_description"]

Related

Is there a similar solution as Array#wrap for hashes?

I use Array.wrap(x) all the time in order to ensure that Array methods actually exist on an object before calling them.
What is the best way to similarly ensure a Hash?
Example:
def ensure_hash(x)
# TODO: this is what I'm looking for
end
values = [nil,1,[],{},'',:a,1.0]
values.all?{|x| ensure_hash(x).respond_to?(:keys) } # true
The best I've been able to come up with so far is:
Hash::try_convert(x) || {}
However, I would prefer something more elegant.
tl; dr: In an app with proper error handling, there is no "easy, care-free" way to handle something that may or may not be hashy.
From a conceptual standpoint, the answer is no. There is no similar solution as Array.wrap(x) for hashes.
An array is a collection of values. Single values can be stored outside of arrays (e.g. x = 42) , so it's a straight-forward task to wrap a value in an array (a = [42]).
A hash is a collection of key-value pairs. In ruby, single key-value pairs can't exist outside of a hash. The only way to express a key-value pair is with a hash: h = { v: 42 }
Of course, there are a thousand ways to express a key-value pair as a single value. You could use an array [k, v] or a delimited string `"k:v" or some more obscure method.
But at that point, you're no longer wrapping, you're parsing. Parsing relies on properly formatted data and has multiple points of failure. No matter how you look at it, if you find yourself in a situation where you may or may not have a hash, that means you need to write a proper chunk of code for data validation and parsing (or refactor your upstream code so that you can always expect a hash).

Datatype conversion error in Ruby for-loop

I'm looking for some help understanding why I get an error (no implicit conversion of nil into String) when attempting to use a for-loop to search through an array of letters (and add them to a resulting string, which seems to be the real problem), but not when I use a while-loop or 'each' for the same purposes. I've looked through a lot of documentation, but haven't been able to find an answer as to why this is happening. I understand that I could just use the "each" method and call it a day, but I'd prefer to comprehend the cause as well as the effect (and hopefully avoid this problem in the future).
The following method works as desired: printing "result" which is the original string, only with "!" in place of any vowels.
s="helloHELLO"
result=""
vowels=["a","e","i","o","u","A","E","I","O","U"]
string_array=s.split("")
string_array.each do |i|
if vowels.include?(i)
result+="!"
else
result+=i
end
end
puts result
However, my initial attempt (posted below) raises the error mentioned above: "no implicit conversion of nil into String" citing lines 5 and 9.
s="helloHELLO"
result=""
vowels=["a","e","i","o","u","A","E","I","O","U"]
string_array=s.split("")
for i in 0..string_array.length
if vowels.include?(string_array[i])
result+= "!"
else
result+=string_array[i]
end
end
puts result
Through experimentation, I managed to get it working; and I determined--through printing to screen rather than storing in "result"--that the problem occurs during concatenation of the target letter to the string "result". But why is "string_array[i]" (line #9) seen as NIL rather than as a String? I feel like I'm missing something very obvious.
If it matters: This is just a kata on CodeWars that lead me to a fundamental question about data types and the mechanics of the for..in loop. This seemed very relevant, but not 100% on the mark for my question: "for" vs "each" in Ruby.
Thanks in advance for the help.
EDIT:
Okay, I think I figured it out. I'd still love some answers though, to confirm, clarify, or downright refute.
I realized that if I wanted to use the for-loop, I should use the array itself as the "range" rather than "0..array.length", like so:
s="helloHELLO"
result=""
vowels=["a","e","i","o","u","A","E","I","O","U"]
string_array=s.split("")
for i in string_array
if vowels.include?(i)
result+= "!"
else
result+=i
end
end
puts result
So, is it that since the "each" method variable (in this case, "i") doesn't exist outside the scope of the main block, its datatype become nil after evaluating whether it's included in the 'vowels' array?
You got beaten by the classical error when iterating an array starting with index 0, instead of length as end position it should be length-1.
But it seems like you come from some other programming language, your code is not Rubyesque, a 'For' for example is seldom used.
Ruby is a higher language than most others, it has many solutions build in, we call it 'sugared' because Ruby is meant to make us programmers happy. What you try to achieve can be done in just one line.
"helloHELLO".scan(/[aeoui]/i).count
Some explanation: the literal array "hello HELLO" is a String, meaning an object of the String class and as such has a lot of methods you can use, like scan, which scans the string for the regular expression /[aeoui]/ which means any of the characters enclosed in the [], the i at the end makes it case insentitive so you don't have to add AEOUI. The scan returns an array with the matching characters, an object of the Array class has the method count, which gives us the ... Yeah once you get the drift it's easy, you can string together methods which act upon each other.
Your for loop:
for i in 0..string_array.length
loops from 0 to 10.
But string[10] #=> nil because there is no element at index 10. And then on line 9 you try to add nil to result
result = result + string_array[i] #expanded
You can't add nil to a string like this, you have to convert nil to a string explicitly thus the error. The best way to fix this issue is to change your for loop to:
for i in 0..string_array.length-1
Then your loop will finish at the last element, string[9].

Maximum arity of ruby function?

I am looking to make an efficient function to clear out a redis-based cache.
I have a method call that returns a number of keys from redis:
$redis.keys("foo:*")
That returns all the keys that start with "foo:". Next, I'd like to delete all the values for these keys.
One (memory-intensive) way to do this is:
$redis.keys("foo:*").each do |key|
$redis.del(key)
end
I'd like to avoid loading all the keys into memory, and then making numerous requests to the redis server.
Another way that I like is to use the splat operator:
keys = $redis.keys("foo:*")
$redis.del(*keys)
The problem is that I don't know what the maximum arity of the $redis.del method, nor of any ruby method, I can't seem to find it online.
What is the maximum arity?
#muistooshort in the comments had a good suggestion that turned out to be right, the redis driver knows what to do with an array argument:
# there are 1,000,000 keys of the form "foo:#{number}"
keys = $redis.keys("foo:*")
$redis.del(keys) # => 1000000
Simply pass an array of keys to $redis.del

Struggling to make sense of an array

So I am trying to make the transition from PHP to ruby(finally). I am attempting to complete the rubymonk challenges but I am stuck on the third challenge.
The challenge itself is easy and I've already found a solution, but I cant figure out what type of data I'm looking at or how to process it properly.
The challenge simply wants you create a method that takes a array containing some strings, and return a count of each string in that same position. so ["I","suck","at","ruby"] == ["1","4","2","4"].
That part is Ez-pz, but I cant for the life of me figure out how to process the input properly.
It gives you a shell of method and tells you to complete it
def lenght_finder(input_array)
#I added the print input_array
print input_array #=> ["I","am","genius"]["things","are","","awesome"]
end
Is this a multidimensional array?
I've tried to replicate this in IRB with
input_array = ["I","am","genius"]["things","are","","awesome"]
but it returns and error
input_array = [["I","am","genius"],["things","are","","awesome"]]
works, but that is clearly not that same.
Because of this I am struggling to traverse the array to process that data properly.
I can't get anything like input_array.flatten to work, or input_array[0] which returns "Ithings".
This is confusing me. Am I looking at a single array? a multidimensional array? Clearly it cant be a string. Why does it skip "am" when accessing input_array[0]?
Ha, like Justin Ko suggested in his comment above, what you're seeing is the stdout of running the function twice.
Since you used print, there's no newline. Use puts instead.
This should help you see it more clearly:
def length_finder(input_array)
puts '*** '+input_array.inspect
return 0
end

Can't all or most cases of `each` be replaced with `map`?

The difference between Enumerable#each and Enumerable#map is whether it returns the receiver or the mapped result. Getting back to the receiver is trivial and you usually do not need to continue a method chain after each like each{...}.another_method (I probably have not seen such case. Even if you want to get back to the receiver, you can do that with tap). So I think all or most cases where Enumerable#each is used can be replaced by Enumerable#map. Am I wrong? If I am right, what is the purpose of each? Is map slower than each?
Edit:
I know that there is a common practice to use each when you are not interested in the return value. I am not interested in whether such practice exists, but am interested in whether such practice makes sense other than from the point of view of convention.
The difference between map and each is more important than whether one returns a new array and the other doesn't. The important difference is in how they communicate your intent.
When you use each, your code says "I'm doing something for each element." When you use map, your code says "I'm creating a new array by transforming each element."
So while you could use map in place of each, performance notwithstanding, the code would now be lying about its intent to anyone reading it.
The choice between map or each should be decided by the desired end result: a new array or no new array. The result of map can be huge and/or silly:
p ("aaaa".."zzzz").map{|word| puts word} #huge and useless array of nil's
I agree with what you said. Enumerable#each simply returns the original object it was called on while Enumerable#map sets the current element being iterated over to the return value of the block, and then returns a new object with those changes.
Since Enumerable#each simply returns the original object itself, it can be very well preferred over the map when it comes to cases where you need to simply iterate or traverse over elements.
In fact, Enumerable#each is a simple and universal way of doing a traditional iterating for loop, and each is much preferred over for loops in Ruby.
You can see the significant difference between map and each when you're composing these enumaratiors.
For example you need to get new array with indixes in it:
array.each.with_index.map { |index, element| [index, element] }
Or for example you just need to apply some method to all elements in array and print result without changing the original array:
m = 2.method(:+)
[1,2,3].each { |a| puts m.call(a) } #=> prints 3, 4, 5
And there's a plenty another examples where the difference between each and map is important key in the writing code in functional style.

Resources