I need a hash key-value pairs to be in the same order as I have assigned. Created Hash in Ruby 1.8:
tmp = {}
tmp["name"] = "laxman"
tmp["age"] = "25"
tmp["city"] = "pune"
tmp # => {"city"=>"pune", "name"=>"laxman", "age"=>"25"}
I need the output:
tmp # => {"name"=>"laxman", "age"=>"25","city"=>"pune"}
Please advise.
Starting from Ruby 1.9 the Hash preserves the order of the keys.
However, if you are using an older version or if for whatever reason the behavior doesn't satisfy you, it's fairly easy to create a custom OrderedHash type that relies on an Array to keep the order of the keys and on a Hash as a storage.
ActiveSupport was used to provide an implementation back in the days where it supported Ruby < 2.0. You can find it here.
Related
I'm trying to grab some data from last.fm and use it in a simple sinatra app. I've worked out how to open the document but having issues extracting the data in ruby here is the first list of the API data I'd like to grab the name:
{"similarartists":{"artist":[{"name":"Sonny & Cher"}]}
This is just an extract of the return, I'm using this in my rb file:
require 'json'
require 'open-uri'
data = JSON.parse(open("http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist=editors&api_key=xxx&format=json").read)
puts data["similarartists"]["artist"]["name"]
It doesn't seem to be working I get can't convert String into Integer (TypeError) on ruby 1.9.3 but the name in the JSON isn't an integer? If I just put the following:
puts data["similarartists"]["artist"]
It returns the whole thing, but I want to grab inside of that and get the name.
"name"=>"Interpol"
I don't understand why it would complain about integers when the name is a string? Hope someone can help me!
Based on the comments thread, the issue is a misunderstanding of the structure of the data returned from the API call.
The exact issue was the structure had an array of artists under the artist key so to get at the name you need to do:
data['similarartists']['artist'][0]['name']
Note though that you should only do that if you are sure there will only be one artist. The nature of the return data suggests that won't always be the case so you might be better off pulling all names depending on your use doing something like:
data['similarartists']['artist'].map {|a| a['name']}.join(',')
That will join all of the artist names together comma separated.
In the future, you can track this issue down by looking at the full structure of the return data and making sure you see the correct structure. The docs on the API may indicate some help here too.
You also might check if someone has made a gem for accessing the API. Often a gem will up-level some of this raw output and give you a nice object to work with. I suggest searching GitHub for a last.fm gem.
The problem is that you are trying to access an Array with the index "name", Ruby tries to convert this to an Integer and fails which results in the Error message you are seeing.
If you test the class of data["similarartists"]["artist"].class you will see that it returns Array. So basically what is happening is that the JSON.parse() called created as the value of data["similarartists"]["artist"] an Array of Hashes. To access all of the artist names you can simply iterate through this array:
require 'json'
require 'open-uri'
data = JSON.parse(open("http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist=editors&api_key=29da5a0e01ca2d1524cac596d5462d67&format=jso\
n").read)
# iterate through the Array of returned artists and print their names
data["similarartists"]["artist"].each do |artist|
puts artist["name"]
end
# output
# Interpol
# White Lies
# The Cinematics
# Smith & Burrows
# The National
# Julian Plenti
# She Wants Revenge
# etc ...
If you only want the first entry for Interpol you can just use index [0]:
puts data["similarartists"]["artist"][0]["name"]
I have two CSV files which have the same structure and ideally should have the same data.
I want to compare the data in them using Ruby and wanted to know if we already have a Ruby function for the same.
If you want to check whether files are identical you can simply use identical? which is an alias for compare_file:
FileUtils.identical?('file1.csv', 'file2.csv')
If you want to see the differences you might want to use diffy:
gem install diffy
puts Diffy::Diff.new('file1.csv', 'file2.csv', :source => 'files')
It produces diff-like output which can be nicely formatted as HTML:
puts Diffy::Diff.new('file1.csv', 'file2.csv', :source => 'files').to_s(:html_simple)
As Summea commented, look at the CSV class.
Then use:
#Will store each line of each file as an array of fields (so an array of arrays).
file1_lines = CSV.read("file1.csv")
file2_lines = CSV.read("file2.csv")
for i in 0..file1_lines.size
if (file1_lines[i] == file2_lines[i]
puts "Same #{file1_lines[i]}"
else
puts "#{file1_lines[i]} != #{file2_lines[i]}"
end
end
Note that using for in Ruby is quite rare. You normally iterate using an each on the collections, but there are two of them here.
Also, pay attention that one of the list may be longer than the other, but this should get you started.
I have been playing around with the projects on learnstreet but I sort of noticed something intresting about the way that they access elements in an array and was hoping to get some clarification.
To access the first element in an array, I know that I can do something like
a = [2,4,5,6,7]
a[0]
output=> 2
However on the learnstreet site they access the first element by doing something like
a = [2,4,5,6,7]
a[0,1]
output => 2
My speculations might be that they are using an older version of ruby that requires that you that. Correct me if I am wrong, am just curious to why it was done that way.
Actually to verify this, I went a step further and tried it in pry but I noticed that using their approach only returned the first element of the array.
My version of ruby is => ruby 1.9.3p327 (2012-11-10 revision 37606) [x86_64-darwin12.2.0]
That is just another way of grabbing the first index saying:
a[0, 1]
Start at the 0 index and grab a slice of length one. This is useful for grabbing a "chunk" or "slice" of the array. Typically, when only involving a certain item of the array, it is clearer to use the single index version. Namely a[0].
See here for more clarification.
The best of grabbing the n number of index values
a[0..1]
It will return 0 index to index 1
eg:- a = [2,4,5,6,7]
a[0..1]
output => [2,4]
It will be neat and clean but it will return the value in array not in string.
I want to preserve the order of the keys in a YAML file loaded from disk, processed in some way and written back to disk.
Here is a basic example of loading YAML in Ruby (v1.8.7):
require 'yaml'
configuration = nil
File.open('configuration.yaml', 'r') do |file|
configuration = YAML::load(file)
# at this point configuration is a hash with keys in an undefined order
end
# process configuration in some way
File.open('output.yaml', 'w+') do |file|
YAML::dump(configuration, file)
end
Unfortunately, this will destroy the order of the keys in configuration.yaml once the hash is built. I cannot find a way of controlling what data structure is used by YAML::load(), e.g. alib's orderedmap.
I've had no luck searching the web for a solution.
Use Ruby 1.9.x. Previous version of Ruby do not preserve the order of Hash keys, but 1.9 does.
If you're stuck using 1.8.7 for whatever reason (like I am), I've resorted to using active_support/ordered_hash. I know activesupport seems like a big include, but they've refactored it in later versions to where you pretty much only require the part you need in the file and the rest gets left out. Just gem install activesupport, and include it as shown below. Also, in your YAML file, be sure to use an !!omap declaration (and an array of Hashes). Example time!
# config.yml #
months: !!omap
- january: enero
- february: febrero
- march: marzo
- april: abril
- may: mayo
Here's what the Ruby behind it looks like.
# loader.rb #
require 'yaml'
require 'active_support/ordered_hash'
# Load up the file into a Hash
config = File.open('config.yml','r') { |f| YAML::load f }
# So long as you specified an !!omap, this is actually a
# YAML::PrivateClass, an array of Hashes
puts config['months'].class
# Parse through its value attribute, stick results in an OrderedHash,
# and reassign it to our hash
ordered = ActiveSupport::OrderedHash.new
config['months'].value.each { |m| ordered[m.keys.first] = m.values.first }
config['months'] = ordered
I'm looking for a solution that allows me to recursively dig through a Hash loaded from a .yml file, look for those YAML::PrivateClass objects, and convert them into an ActiveSupport::OrderedHash. I may post a question on that.
Someone came up with the same issue. There is a gem ordered hash. Note that it is not a hash, it creates a subclass of hash. You might give it a try, but if you see a problem dealing with YAML, then you should consider upgrading to ruby1.9.
I installed *memcache_client* GEM Ruby from http://seattlerb.rubyforge.org/memcache-client/
It's easy to get a single value:
cache.get('foo', 'bar')
How to get all values, starting with 'foo', for example foo_1, foo_2, foo_3, foo_* ?
Something like "SELECT * FROM foo", but for Memcached.
There will be about 10 000 "foo_n" entries.
Not a perfect solution, but look at the get_multi function:
keys = (1..10_000).map{ |n| "foo_#{n}" }
data = cache.get_multi(*keys)
Unfortunately memcached doesn't support regex key lookups, or even let you get a list of all the keys to process on your own. One alternative would be to use Redis which can get a list of keys using a glob style pattern.
Might want to look at Redis as an alternative to memcache. It supports lists, sets, sorted sets and hashes. http://code.google.com/p/redis/