Using the index method, I'm trying to find if a value exists using a certain variable, and if that variable doesn't exist, then try another variable; a little something like this (3rd line below):
a = [ "a", "b", "c" ]
a.index("b") #=> 1
a.index("z" or "y" or "x" or "b") #=> 1
..meaning that if "z" is not found in the array, then try "y"; if y is not found, then try x; if x is not found then try b
How would I do that correctly?
TIMTOWTDI. But I prefer using Array#inject.
%w(x y z b).inject(nil) { |i, e| i or a.index(e) } #=> 1
And there is an another way to do this with more similar to your pseudo-code.
class String
def | other
->(e) { self==e || other==e }
end
end
class Proc
def | other
->(e) { self.call(e) || other==e }
end
end
a.index(&('x' | 'y' | 'z' | 'b')) #=> 1
Depends on your end goal. If you just want to see if a contains a z, y, x or b, you can do this:
(a & %w{z y x b}).length > 0 # gives true if a contains z, y, x and/or b.
what we're doing is seeing if there is a set intersection where a contains some shared elements with the set of desired quantities, then testing to see if there were any of those elements.
Related
I was trying to create a Table of Contents in the command line. Each element in the array is a string variable.
arr = [chap1, chap1_page, chap2, chap2_page, chap3, chap3_page]
x = 0
until x == arr.length
if ((arr[x] != 0 ))
puts arr[x].ljust(line/2) + arr[x += 1].rjust(line/2)
end
x += 1
end
I was wondering if someone could explain the second half of the puts statement. I'm not sure why arr[x+=1] works but arr[x+1] does not. As far as I know, they are the same, are they not?
When dealing with Enumerables like arrays and hashes, it's useful to search the documentation to see if there's something there that will make your code higher level and more expressive. In this case, you can use each_cons to give you the pairs so you don't need to use array indexes at all:
2.3.0 :004 > [1,2,3,4].each_cons(2).to_a
=> [[1, 2], [2, 3], [3, 4]]
Also, rather than using if statements, it's better IMO to use select and reject.
Also, intermediate local variables can make your code more readable.
Using these ideas, your code could look something like this:
array = [chap1, chap1_page, chap2, chap2_page, chap3, chap3_page]
width = line / 2
array.each_cons(2).reject { |x,y| x == 0 }.each do |left, right|
puts left.ljust(width) + right.ljust(width)
end
(I haven't tested this code, but it shows the general idea.)
You could break down those enumerable calls and assign intermediate values to local variables if that makes it clearer for you:
array = [chap1, chap1_page, chap2, chap2_page, chap3, chap3_page]
width = line / 2
pairs = array.each_cons(2)
nonzero_pairs = pairs.reject { |x,y| x == 0 }
nonzero_pairs.each do |left, right|
puts left.ljust(width) + right.rjust(width)
end
x + 1 returns that value and has no side effect (does not change the reference of x). x += 1 reassigns x and returns that value.
I would love to set three variables a, b and c from a single gets line. For example, user entering a line of space-delimited numbers "1 2.2 -3.14" would set a to 1, b to 2.2 and c to -3.14 respectively. I achieve this like so:
input = gets.strip.split
a,b,c = input[0].to_f,input[1].to_f,input[2].to_f
Is there a more elegant way to assign array entries to a range of variables? Perhaps something with a splat and a cycle?
input.each {|entry| *(a..z) = entry }
a,b,c = "1 2.2 -3.14".split.map(&:to_f)
# => [1.0, 2.2, -3.14]
b
# => 2.2
str = "scan may be 1 useful if 2.2 the string may contain -3.14 other stuff"
arr = []
str.scan(/-?\d*(?:\d|\.\d|\d\.)\d*/) { |s| arr << s.to_f }
arr #=> [1.0, 2.2, -3.14]
The regex extracts the embedded numbers that have zero or one decimal points.
Considering that you are using gets to obtain str, I've chosen to use arr = rather than a,b,c = so I can make sure the correct number of floats have been returned:
if arr.size == 3
a,b,c = arr
else
<raise exception>
end
With v1.9+ you could save a line (just saying, not advocating) by using Object#tap:
arr = [].tap { |a| str.scan(/-?\d*(?:\d|\.\d|\d\.)\d*/) { |s| a << s.to_f } }
So say I have a 3D array and I push values like so:
#h = [[[]]]
#h.push(["cat", "mammal", 100.0])
#h.push(["lizard", "reptile", 300.0])
#h.push(["dog", "mammal", 200.0])
How would I reference the max index by the 3rd element (the float) and then reference each individual elements of the index to output in this example just the value "reptile"?
I've tried:
#h.each do |(x,y,z)|
if ( [x,y,z] == #h.max_by{|(x,y,z)| z} )
puts y
end
end
But it doesn't give me only the "y" value.
Thanks!
#h = [] # not [[[]]]
#h.push(["cat", "mammal", 100.0])
#h.push(["lizard", "reptile", 300.0])
#h.push(["dog", "mammal", 200.0])
max_ar = #h.max_by{|ar| ar[2]}
p max_ar[1] #=> "reptile"
# Your way is less usual. It works fine, but you're working too hard:
max_ar = #h.max_by{|(x,y,z)| z}
p max_ar[1] #=> "reptile"
let's say when I'm comparing values in ruby, i have a value in mind that no matter what I want, using sort on that value and anything else returns a -1 (so this value is default sorted as smaller than everything).
for example, let's say i want '100' to sort smaller 100% of the time against 99. so that if i'm sorting values in an array, and a comparison comes up between 100 and 99, 100 is sorted smaller (ie, -1 is returned). but, i want all the other cases to be normal (98 is smaller than 99, 50 is bigger than 30, etc)
edit: okay this is what i want
if i have an x and a y, i do not want to use
x <=> y
i want to use (in pseudocode and hand-wavy-ness)
x > y
which means, this x is always greater than this y
Why don't you instead use a dictionary to keep values associated with their relative value? In this case, the string abc can be mapped to -1, and then just make sure no other values map to values equal to or less than -1.
Edit: If you're only concerned with one particular value breaking the norm, then this solution is not for you.
Easier to handle the specialness outside of the sort!
module Enumerable
def sort_excluding(*vals)
special,rest = partition {|x| vals.include?(x)}
rest.sort + special
end
end
One way to do it would be to implement a derivative class for your custom comparisons (http://www.ruby-doc.org/core/classes/Comparable.html)
Here's some sample code (and tests) for you:
class StrA < String
include Comparable
attr :str
def <=>(anOther)
if (str == "abc" && anOther.str == "abc")
0
elsif (str == "abc")
-1
elsif (anOther.str == "abc")
1
else
str <=> anOther.str
end
end
def initialize(str)
#str = str
end
def inspect
#str
end
end
And the tests:
a = StrA.new("Z")
b = StrA.new("B")
c = StrA.new("abc")
d = StrA.new("")
a > b # 1
a > c # 1
c > a # -1
d > c # 1
c > d # -1
c < d # 1
c > d # -1
[a, b, c, d].sort! # [ "Z", "B", "", "abc"]
I think what you want is:
[30, 50, 4, 0, 100, -22, 99].sort_by {|a| [a == 100 ? -1 : 0, a ]}.reverse
which gives:
99
50
30
4
0
-22
100
Hope I understood the question!
Array#sort or Enumerable#sort(I don't know what you are trying to sort) can take an obtional block. If the block is given, the block is used for comparison instead of <=>
For example this code will sort reversed:
foo.sort { |a,b| b <=> a }
In your case you need to call #sort something like the following:
foo.sort do |a,b|
if [a,b].sort == [99,100]
b-a # returns 1 or -1 so that 99 > 100
else
a <=> b
end
end
I am not entirely sure about the way you are trying to sort, but this should enable you to use sort in the manner you need. More inforamtion about Array#sort, and any other method can be found on your linux(and possible other OS's) via ri like this: ri Array#sort.
You could override the sort method for the object like so:
class Fixnum
alias old_sort <=>
def <=>(object)
if (self == 100 or object == 100)
return -1
else
return self.old_sort object
end
end
end
puts (101 <=> 100)
Edit 1: Above is a fully working example.
Edit 2: As stated by johannes in the comments, you really shouldn't implement this exact code. This is merely a simple example of how to override your sorting method to do domain-specific logic. Also, updated the code to reflect johannes' other comment about comparing with both object and self.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does ||= (or equals) mean in Ruby?
It's hard to search this in Google because it is a symbol, not text.
What does ||= stand for?
And how does it work?
It assigns a value if not already assigned. Like this:
a = nil
a ||= 1
a = 1
a ||= 2
In the first example, a will be set to 1. In the second one, a will still be 1.
From the question Common Ruby Idioms:
is equivalent to
if a == nil || a == false
a = b
end
If b is nil, assign a to it.
a = :foo
b ||= a
# b == :foo
If b is not nil, don't change it.
a = :foo
b = :bar
b ||= a
# b == :bar
This is an 'abbreviated assignment' (see Ruby Pocket Reference, page 10)
a = a || b
(meaning a is assigned the value formed by logical or of a, b
becomes
a ||= b
Almost all operators have an abbreviated version (+= *= &&= etc).
i can only guess, but i assume it stands for an logical operator combined with setting a variable (like ^=, +=, *= in other languages)
so x ||= y is the same as x = x || y
edit: i guessed right, see http://phrogz.net/ProgrammingRuby/language.html#table_18.4
x = x || y means: use x if set, otherwise assign y. it can be used to ensure variables are at least initialised (to 0, to an empty array, etc.)