Accessing elements in a ruby array - ruby

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.

Related

Hash ordered in the way it was created

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.

Python3 Make tie-breaking lambda sort more pythonic?

As an exercise in python lambdas (just so I can learn how to use them more properly) I gave myself an assignment to sort some strings based on something other than their natural string order.
I scraped apache for version number strings and then came up with a lambda to sort them based on numbers I extracted with regexes. It works, but I think it can be better I just don't know how to improve it so it's more robust.
from lxml import html
import requests
import re
# Send GET request to page and parse it into a list of html links
jmeter_archive_url='https://archive.apache.org/dist/jmeter/binaries/'
jmeter_archive_get=requests.get(url=jmeter_archive_url)
page_tree=html.fromstring(jmeter_archive_get.text)
list_of_links=page_tree.xpath('//a[#href]/text()')
# Filter out all the non-md5s. There are a lot of links, and ultimately
# it's more data than needed for his exercise
jmeter_md5_list=list(filter(lambda x: x.endswith('.tgz.md5'), list_of_links))
# Here's where the 'magic' happens. We use two different regexes to rip the first
# and then the second number out of the string and turn them into integers. We
# then return them in the order we grabbed them, allowing us to tie break.
jmeter_md5_list.sort(key=lambda val: (int(re.search('(\d+)\.\d+', val).group(1)), int(re.search('\d+\.(\d+)', val).group(1))))
print(jmeter_md5_list)
This does have the desired effect, The output is:
['jakarta-jmeter-2.5.1.tgz.md5', 'apache-jmeter-2.6.tgz.md5', 'apache-jmeter-2.7.tgz.md5', 'apache-jmeter-2.8.tgz.md5', 'apache-jmeter-2.9.tgz.md5', 'apache-jmeter-2.10.tgz.md5', 'apache-jmeter-2.11.tgz.md5', 'apache-jmeter-2.12.tgz.md5', 'apache-jmeter-2.13.tgz.md5']
So we can see that the strings are sorted into an order that makes sense. Lowest version first and highest version last. Immediate problems that I see with my solution are two-fold.
First, we have to create two different regexes to get the numbers we want instead of just capturing groups 1 and 2. Mainly because I know there are no multiline lambdas, I don't know how to reuse a single regex object instead of creating a second.
Secondly, this only works as long as the version numbers are two numbers separated by a single period. The first element is 2.5.1, which is sorted into the correct place but the current method wouldn't know how to tie break for 2.5.2, or 2.5.3, or for any string with an arbitrary number of version points.
So it works, but there's got to be a better way to do it. How can I improve this?
This is not a full answer, but it will get you far along the road to one.
The return value of the key function can be a tuple, and tuples sort naturally. You want the output from the key function to be:
((2, 5, 1), 'jakarta-jmeter')
((2, 6), 'apache-jmeter')
etc.
Do note that this is a poor use case for a lambda regardless.
Originally, I came up with this:
jmeter_md5_list.sort(key=lambda val: list(map(int, re.compile('(\d+(?!$))').findall(val))))
However, based on Ignacio Vazquez-Abrams's answer, I made the following changes.
def sortable_key_from_string(value):
version_tuple = tuple(map(int, re.compile('(\d+(?!$))').findall(value)))
match = re.match('^(\D+)', value)
version_name = ''
if match:
version_name = match.group(1)
return (version_tuple, version_name)
and this:
jmeter_md5_list.sort(key = lambda val: sortable_key_from_string(val))

Unable to adjust Ruby block

I am fairly new to Ruby, JRuby, etc...
I started to work on migration of certain bash scripts into Ruby, now I used a block from a different pipeline, but here its unnecessary to use it in this format as there is no need to iterate through array:
all_cf = %w(
customers
).map do |table_name|
schema("columns.#{table_name}.hbase.families").gsub(/'/,'')
end.uniq.map{|s| "{ NAME => '#{s}', VERSIONS => 1 }" }.join(',')
Is there an easier way to replace that array iteration and just replace #{table_name} with customers?
I tried this:
all_cf = task do
schema("columns.customers.hbase.families").gsub(/'/,'')
end.uniq.map...
But that just throws error and tried couple of more forms of this, but I think I still don't have a right understanding of the Ruby grammar, as I come from the PHP background I'm still struggling with this, anyone any idea how, and maybe an explanation why?
Cheers...
This produces the result that your original script produced:
"{ NAME => '#{schema("columns.customers.hbase.families").gsub(/'/,'')}', VERSION => 1 }"
I'm not sure what task is, so I don't know whether you should expect your code to work. uniq is a method on Array which returns an Array with all duplicates removed:
> [1,2,3,1,2,1].uniq
# => [1,2,3]
Similarly you can look up what map and join do.

How would I convert this Ruby hash to an array?

I have this data in a hash:
[{"total_time"=>"00:04:48.563044"}, {"total_time"=>"00:05:29.835918"}, {"total_time"=>"00:09:38.622569"}]
But I want this:
["00:04:48.563044", "00:05:29.835918", "00:09:38.622569"]
Needs to work with Ruby 1.8.7.
You might manage with this:
list.collect(&:values).flatten
There's a ton of ways to accomplish this. Let's break it down into the basic steps you need to accomplish:
Iterate over each item in the array of hashes
For each item, grab the time value
Reassemble those into a list
Since you want to grab the result for each item, not just look at it, you'll want to use map (or collect, they're actuality the same method). That will take care of steps 1 and 3. And step 2, by itself, is pretty easy. You just need to get the value for a key with item['total_time']. Put it all together, and you've got this:
times.map{ |time| time['total_time'] }
Speaking about a ton of ways to accomplish this:
a = [{"total_time"=>"00:04:48.563044"}, {"total_time"=>"00:05:29.835918"}, {"total_time"=>"00:09:38.622569"}]
p a.map(&:flatten).map(&:last)

Parsing text files in Ruby when the content isn't well formed

I'm trying to read files and create a hashmap of the contents, but I'm having trouble at the parsing step. An example of the text file is
put 3
returns 3
between
3
pargraphs 1
4
3
#foo 18
****** 2
The word becomes the key and the number is the value. Notice that the spacing is fairly erratic. The word isn't always a word (which doesn't get picked up by /\w+/) and the number associated with that word isn't always on the same line. This is why I'm calling it not well-formed. If there were one word and one number on one line, I could just split it, but unfortunately, this isn't the case. I'm trying to create a hashmap like this.
{"put"=>3, "#foo"=>18, "returns"=>3, "paragraphs"=>1, "******"=>2, "4"=>3, "between"=>3}
Coming from Java, it's fairly easy. Using Scanner I could just use scanner.next() for the next key and scanner.nextInt() for the number associated with it. I'm not quite sure how to do this in Ruby when it seems I have to use regular expressions for everything.
I'd recommend just using split, as in:
h = Hash[*s.split]
where s is your text (eg s = open('filename').read. Believe it or not, this will give you precisely what you're after.
EDIT: I realized you wanted the values as integers. You can add that as follows:
h.each{|k,v| h[k] = v.to_i}

Resources