In this code:
b = ["here", "are", "things"]
b.inject { |str, v| str+="#{v} " }
# => "hereare things "
shouldn't the return value be "here are things"? I assume it's passing the first value to the accumulator str. Is there a way to return "here are things "?
I'd assume it's passing in the first value to the accumulator
Correct, because initial value is not defined, first element of your collection becomes the initial value. Fix? Provide the initial value:
b = ['here', 'are', 'things']
b.inject('') { |memo, elem| memo + "#{elem} " } # => "here are things "
Add the space before the word, like this. This does not have trailing space in the result.
["here", "are", "things"].inject { |str, v| str+=" #{v}" }
#=> "here are things"
You could also do something like below and still not have trailing space
['here', 'are', 'things'].inject { |m, e| "#{m} #{e}" }
#=> "here are things"
Related
Say I have a hash like so:
top_billed = { ghostbusters: 'Bill Murray', star_wars: 'Harrison Ford' }
What would be the best way to format it in a nice, human-readable way?
For example if you called a method on the hash and it displayed the hash as a capitalized list, minus underscores.
"Ghostbusters: Bill Murray
Star Wars: Harrison Ford
I guess iterating over the array and using gsub to remove underscores then capitalizing might work, but I was wondering whether there was anything more elegant.
Thanks
Manually:
top_billed.each do |k, v|
puts k.to_s.gsub("_", " ") + ": " + v
end
if you are using Rails or ActiveSupport, you can also use the "humanize" method (on a String).
Here is a recursive solution:
top_billed = { ghostbusters: { my_name: 'Bill Murray', my_age: 29 }, star_wars: { my_name: 'Harrison Ford' }, something_esle: 'Its Name'}
def print_well(key, value, indent)
key = key.to_s.split('_').map(&:capitalize).join(' ')
if Hash === value
puts "#{key}: "
value.each do |k, v|
print_well k, v, indent + 1
end
else
puts "#{' '*indent}#{key}: #{value}"
end
end
def print_hash hash, indent=0
hash.each do |key, value|
print_well key, value, indent
end
end
print_hash top_billed
As per the question, just wondering how to do this without the use of the Ruby stdlib 'JSON' module (and thus the JSON.pretty_generate method).
So I have an array of hashes that looks like:
[{"h1"=>"a", "h2"=>"b", "h3"=>"c"}, {"h1"=>"d", "h2"=>"e", "h3"=>"f"}]
and I'd like to convert it so that it looks like the following:
[
{
"h1": "a",
"h2": "b",
"h3": "c",
},
{
"h1": "d",
"h2": "e",
"h3": "f",
}
]
I can get the hash-rockets replaced with colon spaces using a simple gsub (array_of_hashes.to_s.gsub!(/=>/, ": ")), but not sure about how to generate it so that it looks like the above example. I had originally thought of doing this use a here-doc approach, but not sure this is the best way, plus i havn't managed to get it working yet either. I'm new to Ruby so apologies if this is obvious! :-)
def to_json_pretty
json_pretty = <<-EOM
[
{
"#{array_of_hashes.each { |hash| puts hash } }"
},
]
EOM
json_pretty
end
In general, working with JSON well without using a library is going to take more than just a few lines of code. That being said, the best way of JSON-ifying things is generally to do it recursively, for example:
def pretty_json(obj)
case obj
when Array
contents = obj.map {|x| pretty_json(x).gsub(/^/, " ") }.join(",\n")
"[\n#{contents}\n]"
when Hash
contents = obj.map {|k, v| "#{pretty_json(k.to_s)}: #{pretty_json(v)}".gsub(/^/, " ") }.join(",\n")
"{\n#{contents}\n}"
else
obj.inspect
end
end
This should work well if you input is exactly in the format you presented and not nested:
a = [{"h1"=>"a", "h2"=>"b", "h3"=>"c"}, {"h1"=>"d", "h2"=>"e", "h3"=>"f"}]
hstart = 0
astart = 0
a.each do |b|
puts "[" if astart == 0
astart+=1
b.each do |key, value|
puts " {" if hstart == 0
hstart += 1
puts " " + key.to_s + ' : ' + value
if hstart % 2 == 0
if hstart == a.collect(&:size).reduce(:+)
puts " }"
else
puts " },\n {"
end
end
end
puts "]" if astart == a.size
end
Output:
[
{
h1 : a
h2 : b
},
{
h3 : c
h1 : d
},
{
h2 : e
h3 : f
}
]
You can take a look at my NeatJSON gem for how I did it. Specifically, look at neatjson.rb, which uses a recursive solution (via a proc).
My code has a lot of variation based on what formatting options you supply, so it obviously does not have to be as complex as this. But the general pattern is to test the type of object supplied to your method/proc, serialize it if it's simple, or (if it's an Array or Hash) re-call the method/proc for each value inside.
Here's a far-simplified version (no indentation, no line wrapping, hard-coded spacing):
def simple_json(object)
js = ->(o) do
case o
when String then o.inspect
when Symbol then o.to_s.inspect
when Numeric then o.to_s
when TrueClass,FalseClass then o.to_s
when NilClass then "null"
when Array then "[ #{o.map{ |v| js[v] }.join ', '} ]"
when Hash then "{ #{o.map{ |k,v| [js[k],js[v]].join ":"}.join ', '} }"
else
raise "I don't know how to deal with #{o.inspect}"
end
end
js[object]
end
puts simple_json({a:1,b:[2,3,4],c:3})
#=> { "a":1, "b":[ 2, 3, 4 ], "c":3 }
I have an Array-1 say
arr1 =['s','a','sd','few','asdw','a','sdfeg']
And a second Array
arr2 = ['s','a','d','f','w']
I want to take arr1 and sort the frequency of letters by inputting arr2 with result
[s=> 4, a=> 2, d => 3] So on and so forth.
As far as I can muddle around.. Nothing below works, Just my thoughts on it?
hashy = Hash.new
print "give me a sentance "
sentance = gets.chomp.downcase.delete!(' ')
bing = sentance.split(//)
#how = sentance.gsub!(/[^a-z)]/, "") #Remove nil result
#chop = how.to_s.split(//).uniq
#hashy << bing.each{|e| how[e] }
#puts how.any? {|e| bing.count(e)}
#puts how, chop
bing.each {|v| hashy.store(v, hashy[v]+1 )}
puts bing
Thank you for your time.
I assumed that you want to count all letters in the sentence you put in, and not array 1. Assuming that, here's my take on it:
hashy = Hash.new()
['s','a','d','f','w'].each {|item| hashy[item.to_sym] = 0}
puts "give me a sentence"
sentence = gets.chomp.downcase.delete!(' ')
sentence_array = []
sentence.each_char do |l|
sentence_array.push(l)
end
hashy.each do |key, value|
puts "this is key: #{key} and value #{hashy[key]}"
sentence_array.each do |letter|
puts "letter: #{letter}"
if letter.to_sym == key
puts "letter #{letter} equals key #{key}"
value = value + 1
hashy[key] = value
puts "value is now #{value}"
end
end
end
puts hashy
Im looking to split an array of strings and creating a hash out of it.
I have an algorithm that splits a string into an array by commas this:1, is:1, a:1, string:1
def split_answer_to_hash(str)
words = str.split(',')
answer = {}
words.each do |w|
a = w.split(':')
h = Hash[ *a.collect { |v| [ v, a[1] ] } ]
answer = h
end
answer
end
What I need to do now is to make the left side of the colon the key to the hash and the right side of the colon the value of the hash. example: {"this" =>1, "is"=>1, "a"=>1, "string"=>1 }
*a.collect is iterating through the array and making the value another key. How can I go about this with out that happening?
The easiest way is:
string = 'this:1, is:1, a:1, string:1'
hash = Hash[*string.split(/:|,/)]
#=> {"this"=>"1", " is"=>"1", " a"=>"1", " string"=>"1"}
Having just one answer to this question just won't do:
str = "this:1, is:1, a:1, string:1"
Hash[str.scan(/(?:([^:]+):(\d+)(?:,\s)?)/)]
.tap { |h| h.keys.each { |k| h[k] = h[k].to_i } }
#=> {"this"=>1, "is"=>1, "a"=>1, "string"=>1}
Object#tap is used merely to convert the values from strings to integers. If you'd prefer:
h = Hash[str.scan(/(?:([^:]+):(\d+)(?:,\s)?)/)]
h.keys.each { |k| h[k] = h[k].to_i }
h
#=> {"this"=>1, "is"=>1, "a"=>1, "string"=>1}
For Ruby 2.1, you can replace Hash[arr] with arr.to_h.
This question already has answers here:
Ruby recursive map of a hash of objects
(2 answers)
Closed 8 years ago.
I have a hash object that could be arbitrarily large - keys are always strings, but values could be strings, arrays, or other hashes. I want to recursively walk through the object and if the value of any particular key (or the value of any array) is a string, I want to strip line endings and leading and trailing whitespace (\r\n, \t, etc")
Do I need to write this algorithm myself or is there some faster ruby-esque way to do it?
You will need to write it yourself. One way to do it is:
def strip_hash_values!(hash)
hash.each do |k, v|
case v
when String
v.strip!
when Array
v.each {|av| av.strip!}
when Hash
strip_hash_values!(v)
end
end
end
This method modifies the original hash:
hash = {:a => [" a ", " b ", " c "], :b => {:x => "xyz "}, :c => "abc "}
strip_hash_values!(hash)
puts hash
# returns {:b=>{:x=>"xyz"}, :c=>"abc", :a=>["a", "b", "c"]}
This is one way to do it.
Code
def strip_strings(o)
case o
when Hash
o.each do |k,v|
o[k] = case v
when String
v.strip
else
strip_strings(v)
end
end
else # Array
o.map do |e|
case e
when String
e.strip
else
strip_strings(e)
end
end
end
end
Example
h = { a: [b: { c: " cat ", d: [" dog ", {e: " pig " }] }], f: " pig " }
#=> {:a=>[{:b=>{:c=>" cat ", :d=>[" dog ", {:e=>" pig "}]}}], :f=>" pig "}
strip_strings(h)
#=> {:a=>[{:b=>{:c=>"cat", :d=>["dog", {:e=>"pig"}]}}], :f=>"pig"}