Delete an array from a multi-dimensional array in ruby - ruby

I have a multi-dimensional array of this form:
array = [["http://domain.com/product.html", 10], ["http://domain.com/product.html", 150], ["http://domain.com/product.html", 500]]
I need to delete all arrays that have the last value smaller than 150.
I already tried the following, but it doesn't seem to have any effect:
array.delete_if {|element| element.last < 150 }
Any help will be highly appreciated. Thanks.

I would probably do it this way:
array.reject!{|x| x if x.last < 150}

You can also use this:
array.map{|f| f if f.last < 150}.compact
I don't know if it is better or not than Akarsh, just another solution that I would have used.
Anyways, your solution works, user3493101, but if it doesn't, you can still use that.

Related

What is the most elegant way to sort ranges in ruby

I need to sort a table of objects of type Rangeby their start point. For that I have the following code which works fine:
ranges = #ranges.sort do |a,b|
(a.min) <=> (b.min)
end
I was just wondering if there was a shorter and elegant way to do the same thing.
How about:
ranges = #ranges.sort_by(&:min)
Or if you actually mean the starting point rather than the minimum, since ranges such as (5..3) can exist:
ranges = #ranges.sort_by(&:first)

re-organizing Array (Transpose)

For an array that looks like:
arr = [["name1","name2","name3"],["address1","address2","address3"],["phone1","phone2","phone3"]]
I would like to re-arrange it so that it looks like:
arr = [["name1","address1","phone1"],["name2","address2","phone2"], ...
Current method is:
name = arr[0]
add = arr[1]
phone = arr[2]
arr = name.zip(add,phone)
which works, but when I have over ten nested arrays within an array, I have ten lines of defining which is which, just to use zip later.
I hope someone can show me a better way of handling this.
EDIT:
I originally had "Phone1","Phone2", as my initial array (uppercase) and "phone1", "phone2" as my transposed array.
This wasn't intended so I edited it, but with my original post Sawa's answer handles the transpose & the UPPERCASE to lowercase.
Also found the documentation here:
http://www.ruby-doc.org/core-2.1.2/Array.html#method-i-transpose
An answer to the original question:
arr.transpose.map{|a| a.map(&:downcase)}
An answer to a different question after OP's edit:
arr.transpose
How about:
arr = arr.shift.zip(*arr)
this code uses the first element of the arr while removing it from arr (via shift), than it uses the splat operator to zip it with the rest of the arrays in the array.

Is there an equivalent to `Array::sample` for hashes?

I'm looking to extract n random key-value pairs from a hash.
Hash[original_hash.to_a.sample(n)]
For Ruby 2.1,
original_hash.to_a.sample(n).to_h
I don't know of such method. Still you can do something like:
h[h.keys.sample]
If you need to sample more than one element the code will have to be a bit more complicated.
EDIT: to get key value pairs instead of only the value you can do something like:
keys_sample = h.keys.sample(n)
keys_sample.zip(keys_sample.map{|k| h[k])
Reading the top ranked answers, I'd go with it depends:
If you want to sample only one element from the hash, #Ivaylo Strandjev's solution only relies on hash lookup and Array#sample:
hsh[hsh.keys.sample]
To sample multiple hash elements, #sawa's answer leverages Array#to_h:
hsh.to_a.sample(n).to_h
Note that, as #cadlac mentions, hsh.to_a.sample.to_h won't work as expected. It will raise
TypeError: wrong element type String at 0 (expected array)
because Array#sample in this case returns just the element array, and not the array containing the element array.
A workaround is his solution, providing an n = 1 as an argument:
hsh.to_a.sample(1).to_h
PS: not looking for upvotes, only adding it as an explanation for people newer to Ruby.
If your sample has only one element, you could use this:
sample = h.keys.sample
h.select { |k,v| k == sample }
Or if your sample contains more than one element, use this:
n = 2
sample = h.keys.sample(n)
h.select { |k,v| sample.include?(k) }
One way to accomplish this:
rank_hash = {"Listen" => 1, "Download" => 60, "Share" => 150, "Purchase" => 700 }
rank_array = rank_hash.to_a
Than call this to get random array sample of the k/v pair:
rank_array[rand(0..3)]
or this to not hard-code the arrays length:
rank_array[rand(0..(rank_array.length) -1)]
Example:
["Download", 60]

Ruby: how to find the next match in an array

I have to search an item in an array and return the value of the next item. Example:
a = ['abc.df','-f','test.h']
i = a.find_index{|x| x=~/-f/}
puts a[i+1]
Is there any better way other than working with index?
A classical functional approach uses no indexes (xs.each_cons(2) -> pairwise combinations of xs):
xs = ['abc.df', '-f', 'test.h']
(xs.each_cons(2).detect { |x, y| x =~ /-f/ } || []).last
#=> "test.h"
Using Enumerable#map_detect simplifies it a litte bit more:
xs.each_cons(2).map_detect { |x, y| y if x =~ /-f/ }
#=> "test.h"
The reason something like array.find{something}.next doesn't exist is that it's an array rather than a linked list. Each item is just it's own value; it doesn't have a concept of "the item after me".
#tokland gives a good solution by iterating over the array with each pair of consecutive items, so that when the first item matches, you have your second item handy. There are strong arguments to be made for the functional style, to be sure. Your version is shorter, though, and I'd argue that yours is also more quickly and easily understood at a glance.
If the issue is that you're using it a lot and want something cleaner and more to the point, then of course you could just add it as a singleton method to a:
def a.find_after(&test)
self[find_index(&test).next]
end
Then
a.find_after{|x| x=~/-f/}
is a clear way to find the next item after the first match.
All of that said, I think #BenjaminCox makes the best point about what appears to be your actual goal. If you're parsing command line options, there are libraries that do that well.
I don't know of a cleaner way to do that specific operation. However, it sure looks like you're trying to parse command-line arguments. If so, I'd recommend using the built-in OptionParser module - it'll save a ton of time and hair-pulling trying to parse them yourself.
This article explains how it works.
Your solution working with indexes is fine, as others have commented. You could use Enumerable#drop_while to get an array from your match on and take the second element of that:
a = ['abc.df','-f','test.h']
f_arg = a.drop_while { |e| e !~ /-f/ }[1]

Easiest way to convert "a/b/c" to ["a/b/c", "a/b", "a"]

In Ruby, I'd like to convert a slash-separate String such as "foo/bar/baz" into ["foo/bar/baz", "foo/bar", "foo"]. I already have solutions a few lines long; I'm looking for an elegant one-liner. It also needs to work for arbitrary numbers of segments (0 and up).
"foo/bar/baz".enum_for(:scan, %r{/|$}).map {Regexp.last_match.pre_match}
The highest voted answer works, but here is a slightly shorter way to do it that I think will be more readable for those not familiar with all the features used there:
a=[]; s.scan(/\/|$/){a << $`}
The result is stored in a:
> s = 'abc/def/ghi'
> a=[]; s.scan(/\/|$/){a << $`}
> a
["abc", "abc/def", "abc/def/ghi"]
If the order is important, you can reverse the array or use unshift instead of <<.
Thanks to dkubb, and to the OP for the improvements to this answer.
Not quite as efficient as the chosen answer, and gives [] when given an empty string, rather than [""], but its a real one liner :P
s.split('/').inject([]) { |a,d| a.unshift( [a.first,d].compact.join('/') ) }
->(l,s,z){
( t = s[/#{z}.[^\/]*/] ) && [l[l,s,t], t]
}.tap{ |l|
break l[l,'a/b/c','']
}.flatten.compact

Resources