This question already has answers here:
How can I check if a Ruby array includes one of several values?
(5 answers)
Closed 8 years ago.
I have an array a = ["1","2","3","6","7"] and another array b = ["2","4","7"]. I want to check if any content of b exists in a or not.
You can do
a = ["1","2","3","6","7"]
b = ["2","4","7"]
b.any? { |e| a.include?(e) }
that is so simple:
(a & b).blank?
actually what does it do is, it takes intersection of two array and returns the result then check if the result is blank/empty.
Use & operator of Ruby, it will return an array with intersection values of two arrays, below is an example.
pry(main)> a = ["1","2","3","6","7"]
=> ["1", "2", "3", "6", "7"]
pry(main)> b = ["2","4","7"]
=> ["2", "4", "7"]
pry(main)> a & b
=> ["2", "7"]
pry(main)> (a & b).empty?
=> false
In Rails, you can also use blank?
pry(main)> (a & b).blank?
=> false
Hope the above example helps
Related
ids = "1,4,5,"
ids.split(',') => ["1", " 4", " 5", " "]
ids.split(',').map(&:to_i) => [1, 4, 5, 0]
How do I remove that empty value before it becomes a zero?
You can use #scan also
ids = "1,4,5,"
ids.scan(/\d+/).map(&:to_i)
# => [1, 4, 5]
This doesn't happen in Ruby 2.2+:
ids = "1,4,5,"
ids.split(',')
# => ["1", "4", "5"]
RUBY_VERSION # => "2.2.0"
The simple thing to do is run a preflight check on your data and normalize it to what it's supposed to be, BEFORE trying to process it:
ids = "1,4,5,"
ids.chop! if ids[-1] == ','
ids # => "1,4,5"
ids.split(',')
# => ["1", "4", "5"]
You could be a bit more rigorous in the test, since the end of the line might also contain whitespace which would throw off the cleanup.
Also, you're dealing with comma-delimited data, so consider using the built in CSV class, which is designed to work with such strings.
I am trying to replace a component in a legacy system with a Ruby script. One piece of this system accepts a string that contains ASCII '0's and '1's apparently to represent a bitfield of locations. It then converts these location to a string of comma separated 2 two codes (mostly US states).
I have a Ruby method that does this but it doesn't seem like I am doing it the best way Ruby could. Ruby has a ton of ways built in to iterate over and manipulated array and I feel I am not using them to their fullest:
# input "0100010010" should return "AZ,PR,WY"
def locations(bits)
# Shortened from hundreds for this post. :u? is for locations I have't figured out yet.
fields = [ :u?, :az, :de, :mi, :ne, :wy, :u?, :u?, :pr, :u? ]
matches = []
counter = 0
fields.each { |f|
case bits[counter]
when '1' then matches << f
when '0' then nil
else raise "Unknown value in location bit field"
end
counter += 1
}
if matches.include(:u?) then raise "Unknown field bit set" end
matches.sort.join(",").upcase
end
What would be a better way to do this?
It seems counter to the "Ruby way" to have counter variables floating around. I tried looking at ways to use Array#map, and I could find nothing obvious. I also tried Googling for Ruby Idioms pertaining to Arrays.
matches = fields.select.with_index { |_,i| bits[i] == '1' }
# => [:az, :wy, :pr]
To verify bits only holds 0s and 1s, you can still do
raise "Unknown value in location bit field" if !bits.match(/^[01]*$/)
Use Array#zip and Array#reduce
bits.split('').zip(fields).reduce([]) do |a, (k, v)|
k == '1' ? a << v.to_s.upcase : a
end.sort.join(',')
# => "AZ,PR,WY
Explanation:
1) split bits into an array of chars:
bits.split('') # => ["0", "1", "0", "0", "0", "1", "0", "0", "1", "0"]
2) zip both arrays to generate an array of pairs (by position)
bits.split('').zip(fields) # => [["0", :u?], ["1", :az], ["0", :de], ["0", :mi],
# ["0", :ne], ["1", :wy], ["0", :u?], ["0", :u?], ["1", :pr], ["0", :u?]]
3) reduce the array taking the desired elements according to the conditions
.reduce([]) do |a, (k, v)|
k == '1' ? a << v.to_s.upcase : a
end # => "[AZ,WY,PR]
4) sort the resulting array and join their elements to get the expected string
.sort.join(',') # => "AZ,PR,WY"
You could combine each_with_index, map andcompact:
fields.each_with_index.map do |v,i|
v if bits[i] == '1'
end.compact
each_with_index returns an iterator for each each value and its integer index.
map uses the return value of a passed block to yield an output value for each input value. The block returns the value if its corresponding bit is set, and implicitly returns nil if it is not.
compact returns a copy of the output array with all of the nil values removed.
For more detail see the docs for Enumerable and Array.
I have an array that I am looping through and pushing specific values to a separate array. EX:
first_array = ["Promoter: 8", "Passive: 7"]
I want to push every value that is an integer to a separate array, that would look like this in the end:
final_array = [8,7]
It would be nice for the values in the new array to be integers. I can't think of a way to push all numeric values within a string to a new array, but what would be the best option to do what I am wanting?
first_array.map{|s| s[/\d+/].to_i}
# => [8, 7]
first_array.map{|a| a.match(/\d+/)}.compact.map{|a| a[0].to_i }
Use a regex to grab the integers,
compact the blank spaces from the strings with no integers, and
convert them all to ints
And I have to add this super short but complicated one-liner solution:
a = ["Promoter: 8", "Passive: 7"]
p a.grep(/(\d+)/){$&.to_i} #=> [8,7]
Your question, as formulated, has an easy practical answer, already provided by others. But it seems to me, that your array of strings
a = ["Promoter: 8", "Passive: 7"]
envies being a Hash. So, from broader perspective, I would take freedom of converting it to a Hash first:
require 'pyper' # (type "gem install pyper" in your command line to install it)
hsh = Hash[ a.τBmm2dτ &/(\w+): *(\d+)/.method( :match ) ]
#=> {"Promoter"=>"8", "Passive"=>"7"}
# (The construction of #τBmm2dτ Pyper method will be explained in the appendix.)
Now, having your input data in a hash, you can do things with them more easily, eg.
hsh.τmbtiτ
#=> [8, 7]
APPENDIX: Explanation of the Pyper methods.
Pyper methods are similar to Lisp #car/#cdr methods in that, that a combination of
letters controls the method behavior. In the first method, #τBmm2dτ:
τ - opening and ending character
m - means #map
B - means take a block
2 - means first 3 elements of an array
d - means all elements except the first one (same meaning as in #cdr, btw.)
So, in #τBmm2dτ, Bm applies the block as follows:
x = ["Promoter: 8", "Passive: 7"].map &/(\w+): *(\d+)/.method( :match )
#=> [#<MatchData "Promoter: 8" 1:"Promoter" 2:"8">, #<MatchData "Passive: 7" 1:"Passive" 2:"7">]
# Which results in an array of 2 MatchData objects.
Then, m2d chars map (m) the MatchData objects using 2 and d chars. Character 2 gives
x = x.map { |e| e.to_a.take 3 }
#=> [["Promoter: 8", "Promoter", "8"], ["Passive: 7", "Passive", "7"]]
and d removes the first element from each:
x = x.map { |e| e.drop 1 }
#=> [["Promoter", "8"], ["Passive", "7"]]
In the secon method, #τmbtiτ, m means again #map, b means take the second element, and ti means convert it to Integer:
{"Promoter"=>"8", "Passive"=>"7"}.to_a.map { |e| Integer e[1] }
#=> [8, 7]
If the integer part of each string in (which look like members of a hash) is always preceded by at least one space, and there is no other whitespace (other than possibly at the beginning of the string), you could do this:
first_array = ["Promoter: 8", "Passive: 7"]
Hash[*first_array.map(&:split).flatten].values.map(&:to_i) # => [8,7]
map first_array => [["Promoter:", "8"], ["Passive:", "7"]]
flatten => ["Promoter:", "8", "Passive:", "7"]
convert to hash => {"Promoter:" => "8", "Passive:" => "7"}
get hash values => ["8", "7"]
convert to ints => [8, 7]
Note the need for the splat:
Hash[*["Promoter:", "8", "Passive:", "7"]]
=> Hash["Promoter:", "8", "Passive:", "7"]
=> {"Promoter:" => "8", "Passive:" => "7"}
I'm having issues creating a hash from 2 arrays when values are identical in one of the arrays.
e.g.
names = ["test1", "test2"]
numbers = ["1", "2"]
Hash[names.zip(numbers)]
works perfectly it gives me exactly what I need => {"test1"=>"1", "test2"=>"2"}
However if the values in "names" are identical then it doesn't work correctly
names = ["test1", "test1"]
numbers = ["1", "2"]
Hash[names.zip(numbers)]
shows {"test1"=>"2"} however I expect the result to be {"test1"=>"1", "test1"=>"2"}
Any help is appreciated
Hashes can't have duplicate keys. Ever.
If they were permitted, how would you access "2"? If you write myhash["test1"], which value would you expect?
Rather, if you expect to have several values under one key, make a hash of arrays.
names = ["test1", "test1", "test2"]
numbers = ["1", "2", "3"]
Hash.new.tap { |h| names.zip(numbers).each { |k, v| (h[k] ||= []) << v } }
# => {"test1"=>["1", "2"], "test2"=>["3"]}
If I manually create an array with
array = ["2","1","3"]
array.sort will return a sorted version of the array.
But if I create an array by using
array2 = ["213".split(//)]
or
array2 = []
array2 << "213".split(//)
array2.sort will return the unsorted array.
Why doesn't this work? Are the arrays created like that somehow different, and if yes, how?
The expression "213".split(//) already returns an array, so in both cases you're actually creating an array and adding that entire thing as the first element in a new array.
["213".split(//)]
=> [["2", "1", "3"]]
array2 << "213".split(//)
=> [["2", "1", "3"]]
Notice the double brackets. Sorting this array has no effect, because it contains just one element (itself an array). You want to remove the surrounding brackets:
"213".split(//)
=> ["2", "1", "3"]
You define array2 as ["213".split(//)]. This puts an array (["2","1","3"]) inside an array. The output is:
array = ["213".split(//)]
=> [["2", "1", "3"]]
When you try and sort that, it sorts the "bigger" array: the one with one element!
This works, though:
array = "213".split(//)
=> ["2", "1", "3"]
array.sort
=> ["1", "2", "3"]
This bit of code:
array2 = ["213".split(//)]
is creating an array with one element that is an array. To sort the array that you are interested in:
array2[0].sort
But, more likely, you should remove the outer brackets to avoid creating the array of arrays. The same applies to this:
array2 = []
array2 << "213".split(//)
It creates an empty array and then adds an array element which happens to be another array.
In the first case, you're creating an array whose elements are "2", "1", and "3". In the second case, you're creating an array that contains an array whose elements are "2", "1", and "3":
ruby-1.8.7-p334 :003 > array = ["2","1","3"]
=> ["2", "1", "3"] # Notice that this is an array of three elements
ruby-1.8.7-p334 :001 > array2 = ["213".split(//)]
=> [["2", "1", "3"]] #...but this is an array of an array of three elements
ruby-1.8.7-p334 :005 > array2 = "213".split(//)
=> ["2", "1", "3"] # Remove those extra brackets and it's equivalent to the first case.
If you're read this far, you probably figured out that in the second case, you were just sorting a single-element array and seeing that single element, which was unchanged.