Given:
a1 = [5, 1, 6, 14, 2, 8]
I would like to determine if it contains all elements of:
a2 = [2, 6, 15]
In this case the result is false.
Are there any built-in Ruby/Rails methods to identify such array inclusion?
One way to implement this is:
a2.index{ |x| !a1.include?(x) }.nil?
Is there a better, more readable, way?
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]
a - b
# => [5, 1, 14, 8]
b - a
# => [15]
(b - a).empty?
# => false
Perhaps this is easier to read:
a2.all? { |e| a1.include?(e) }
You can also use array intersection:
(a1 & a2).size == a1.size
Note that size is used here just for speed, you can also do (slower):
(a1 & a2) == a1
But I guess the first is more readable. These 3 are plain ruby (not rails).
This can be achieved by doing
(a2 & a1) == a2
This creates the intersection of both arrays, returning all elements from a2 which are also in a1. If the result is the same as a2, you can be sure you have all elements included in a1.
This approach only works if all elements in a2 are different from each other in the first place. If there are doubles, this approach fails. The one from Tempos still works then, so I wholeheartedly recommend his approach (also it's probably faster).
If there are are no duplicate elements or you don't care about them, then you can use the Set class:
a1 = Set.new [5, 1, 6, 14, 2, 8]
a2 = Set.new [2, 6, 15]
a1.subset?(a2)
=> false
Behind the scenes this uses
all? { |o| set.include?(o) }
You can monkey-patch the Array class:
class Array
def contains_all?(ary)
ary.uniq.all? { |x| count(x) >= ary.count(x) }
end
end
test
irb(main):131:0> %w[a b c c].contains_all? %w[a b c]
=> true
irb(main):132:0> %w[a b c c].contains_all? %w[a b c c]
=> true
irb(main):133:0> %w[a b c c].contains_all? %w[a b c c c]
=> false
irb(main):134:0> %w[a b c c].contains_all? %w[a]
=> true
irb(main):135:0> %w[a b c c].contains_all? %w[x]
=> false
irb(main):136:0> %w[a b c c].contains_all? %w[]
=> true
irb(main):137:0> %w[a b c d].contains_all? %w[d c h]
=> false
irb(main):138:0> %w[a b c d].contains_all? %w[d b c]
=> true
Of course the method can be written as a standard-alone method, eg
def contains_all?(a,b)
b.uniq.all? { |x| a.count(x) >= b.count(x) }
end
and you can invoke it like
contains_all?(%w[a b c c], %w[c c c])
Indeed, after profiling, the following version is much faster, and the code is shorter.
def contains_all?(a,b)
b.all? { |x| a.count(x) >= b.count(x) }
end
Most answers based on (a1 - a2) or (a1 & a2) would not work if there are duplicate elements in either array. I arrived here looking for a way to see if all letters of a word (split to an array) were part of a set of letters (for scrabble for example). None of these answers worked, but this one does:
def contains_all?(a1, a2)
try = a1.chars.all? do |letter|
a1.count(letter) <= a2.count(letter)
end
return try
end
Depending on how big your arrays are you might consider an efficient algorithm O(n log n)
def equal_a(a1, a2)
a1sorted = a1.sort
a2sorted = a2.sort
return false if a1.length != a2.length
0.upto(a1.length - 1) do
|i| return false if a1sorted[i] != a2sorted[i]
end
end
Sorting costs O(n log n) and checking each pair costs O(n) thus this algorithm is O(n log n). The other algorithms cannot be faster (asymptotically) using unsorted arrays.
I was directed to this post when trying to find whether one array ["a", "b", "c"] contained another array ["a", "b"], where in my case identical ordering was an additional requirement to the question.
Here is my solution (I believe it's O(n) complexity), to anyone who has that extra requirement:
def array_includes_array(array_to_inspect, array_to_search_for)
inspectLength = array_to_inspect.length
searchLength = array_to_search_for.length
if searchLength == 0 then
return true
end
if searchLength > inspectLength then
return false
end
buffer = []
for i in 0..inspectLength
buffer.push(array_to_inspect[i])
bufferLastIndex = buffer.length - 1
if(buffer[bufferLastIndex] != array_to_search_for[bufferLastIndex]) then
buffer.clear
next
end
if(buffer.length == searchLength) then
return true
end
end
return false
end
This produces the test results:
puts "1: #{array_includes_array(["a", "b", "c"], ["b", "c"])}" # true
puts "2: #{array_includes_array(["a", "b", "c"], ["a", "b"])}" # true
puts "3: #{array_includes_array(["a", "b", "c"], ["b", "b"])}" # false
puts "4: #{array_includes_array(["a", "b", "c"], ["c", "b", "a"])}" # false
puts "5: #{array_includes_array(["a", "b", "c"], [])}" # true
puts "6: #{array_includes_array([], ["a"])}" # false
puts "7: #{array_includes_array([], [])}" # true
Related
I'm learning Lua and coming from Python Lua tables seem rather convoluted, the simple example below is so elegant but translating this into Lua is difficult for me, as Lua has no concept of tuples.
So I'm looking for the best Lua solution for this snippet
a = [(1, 1), (2, 2), (3, 3), (4, 4)]
if (3, 3) in a:
print("Yay!")
else:
print("Nay!")
A pair is just like length-2 list, so you could express a simply as
a = {{1, 1}, {2, 2}, {3, 3}, {4, 4}}
The tricky part here is rather that Python's in compares objects by their value rather than their identity. That is, in Python, (1,2) in [(1,2)] but (1,2) is not (1,2).
Lua has no notion of "value" equality (except for strings and numbers, which don't have identities).
You could override the behavior of == by setting the __eq metametod. Unfortunately, Lua doesn't have a function for searching a table for a value equal to some query, so it might be overkill.
Directly, you could write a "contains pair" function that works on the a as defined above like this:
function containsPair(list, pair)
-- Find a key of list with a value equal to `pair` (as a pair)
for k, v in ipairs(list) do
if v[1] == pair[1] and v[2] == pair[2] then
return k
end
end
end
if containsPair(a, {3, 3}) then
......
end
You could make it more general by passing a function do the comparison (or equivalently, just use == but implement the __eq metamethod):
function containsLike(list, lhs, eq)
-- Find a key of list with a value equal to lhs
for k, lhs in ipairs(list) do
if eq(lhs, rhs) then
return k
end
end
end
function pairEq(a, b)
return a[1] == b[1] and a[2] == b[2]
end
if containsLike(list, {3, 3}, pairEq) then
......
end
If what you're really after is a set of pairs, you could instead use a "two-dimensional map" (a map of maps):
a = {}
a[1] = {}
a[1][1] = true
a[2] = {}
a[2][2] = true
a[3] = {}
a[3][3] = true
if a[3] and a[3][3] then
......
end
Checking that rows have already been created could be cumbersome. You can use metatables to imitate Python's defaultdict and clean this up:
function default(f)
return setmetatable({}, {
__index = function(self, k)
-- self[k] is nil, but was asked for.
-- Let's assign it to the default value:
self[k] = f()
-- and return the new assignment:
return self[k]
end,
})
end
local a = default(function() return {} end)
a[1][1] = true
a[2][2] = true
a[3][3] = true
if a[3][3] then
......
end
I have a hash of data that is holding different strings as its Key. I need to create a new method in my class that will count the number of vowels in each key and then return the key with the most vowels. I am very stuck and this is what I have so far.
def favorite_wish
vowels = ["a", "e", "i", "o", "u"]
#submitted_wishes.each_key do |wish|
wish.split(' ')
wish.each do |check|
if check == vowels
end
end
end
Can anyone help?
String#count might help you:
# this will return the key with the max number of vowels
def favorite_wish
#submitted_wishes.keys.max_by { |wish| wish.count('aeiou') }
end
# this will return the value to the key with the max number of vowels
def favorite_wish
max_key = #submitted_wishes.keys.max_by { |wish| wish.count('aeiou') }
#submitted_wishes[max_key]
end
This will get the key with the most vowels:
#submitted_wishes.keys.max_by { |key| key.count('aeiou') }
I would use the following methods:
def count_vowels(str)
str.count 'aeiou'
end
def highest_value_key(hash)
hash.key(hash.values.max)
end
The idea behind these methods is to separate concerns and make it more readable.
h = { "Mary"=>"Mary", "quite"=>"contrary", "how"=>"does your", "garden"=>"grow?" }
h.map { |k,_| [k.count('aeiou'), k] }.max.last
#=> => "quite"
The steps:
a = h.map { |k,_| [k.count('aeiou'), k] }
#=> [[1, "Mary"], [3, "quite"], [1, "how"], [2, "garden"]]
b = a.max
#=> [3, "quite"]
b.last
#=> "quite"
See Array#<=> for an explanation of how arrays are ordered (when computing max).
If keys k1 and k2 tie for the maximum number of vowels, k1 <=> k2 breaks the tie (k1 is returned if k1 <=> k2 #=> -1, k2 is returned if k1 <=> k2 #=> 1, either key might be returned if k1 <=> k2 #=> 0. See String#<=>.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
There's two arrays of hash and I want remove the 'common' elements from the two arrays, based on certain keys. For example:
array1 = [{a: '1', b:'2', c:'3'}, {a: '4', b: '5', c:'6'}]
array2 = [{a: '1', b:'2', c:'10'}, {a: '3', b: '5', c:'6'}]
and the criteria keys are a and b. So when I get the result of something like
array1-array2 (don't have to overwrite '-' if there's better approach)
it will expect to get
[{a: '4', b: '5', c:'6'}]
sine we were using a and b as the comparing criteria. It will wipe the second element out since the value for a is different for array1.last and array2.last.
As I understand, you are given two arrays of hashes and a set of keys. You want to reject all elements (hashes) of the first array whose values match the values of any element (hash) of the second array, for all specified keys. You can do that as follows.
Code
require 'set'
def reject_partial_dups(array1, array2, keys)
set2 = array2.each_with_object(Set.new) do |h,s|
s << h.values_at(*keys) if (keys-h.keys).empty?
end
array1.reject do |h|
(keys-h.keys).empty? && set2.include?(h.values_at(*keys))
end
end
The line:
(keys-h.keys).empty? && set2.include?(h.values_at(*keys))
can be simplified to:
set2.include?(h.values_at(*keys))
if none of the values of keys in the elements (hashes) of array1 are nil. I created a set (rather than an array) from array2 in order to speed the lookup of h.values_at(*keys) in that line.
Example
keys = [:a, :b]
array1 = [{a: '1', b:'2', c:'3'}, {a: '4', b: '5', c:'6'}, {a: 1, c: 4}]
array2 = [{a: '1', b:'2', c:'10'}, {a: '3', b: '5', c:'6'}]
reject_partial_dups(array1, array2, keys)
#=> [{:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}]
Explanation
First create set2
e0 = array2.each_with_object(Set.new)
#=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"10"}, {:a=>"3", :b=>"5", :c=>"6"}]
# #:each_with_object(#<Set: {}>)>
Pass the first element of e0 and perform the block calculation.
h,s = e0.next
#=> [{:a=>"1", :b=>"2", :c=>"10"}, #<Set: {}>]
h #=> {:a=>"1", :b=>"2", :c=>"10"}
s #=> #<Set: {}>
(keys-h.keys).empty?
#=> ([:a,:b]-[:a,:b,:c]).empty? => [].empty? => true
so compute:
s << h.values_at(*keys)
#=> s << {:a=>"1", :b=>"2", :c=>"10"}.values_at(*[:a,:b] }
#=> s << ["1","2"] => #<Set: {["1", "2"]}>
Pass the second (last) element of e0 to the block:
h,s = e0.next
#=> [{:a=>"3", :b=>"5", :c=>"6"}, #<Set: {["1", "2"]}>]
(keys-h.keys).empty?
#=> true
so compute:
s << h.values_at(*keys)
#=> #<Set: {["1", "2"], ["3", "5"]}>
set2
#=> #<Set: {["1", "2"], ["3", "5"]}>
Reject elements from array1
We now iterate through array1, rejecting elements for which the block evaluates to true.
e1 = array1.reject
#=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"3"},
# {:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}]:reject>
The first element of e1 is passed to the block:
h = e1.next
#=> {:a=>"1", :b=>"2", :c=>"3"}
a = (keys-h.keys).empty?
#=> ([:a,:b]-[:a,:b,:c]).empty? => true
b = set2.include?(h.values_at(*keys))
#=> set2.include?(["1","2"] => true
a && b
#=> true
so the first element of e1 is rejected. Next:
h = e1.next
#=> {:a=>"4", :b=>"5", :c=>"6"}
a = (keys-h.keys).empty?
#=> true
b = set2.include?(h.values_at(*keys))
#=> set2.include?(["4","5"] => false
a && b
#=> false
so the second element of e1 is not rejected. Lastly:
h = e1.next
#=> {:a=>1, :c=>4}
a = (keys-h.keys).empty?
#=> ([:a,:c]-[:a,:b]).empty? => [:c].empty? => false
so return true (meaning the last element of e1 is not rejected), as there is no need to compute:
b = set2.include?(h.values_at(*keys))
So you really should try this out yourself because I am basically solving it for you.
The general approach would be:
For every time in array1
Check to see the same value in array2 has any keys and values with the same value
If they do then, delete it
You would probably end up with something like array1.each_with_index { |h, i| h.delete_if {|k,v| array2[i].has_key?(k) && array2[i][k] == v } }
I have a column called "Marks" which contains values like
Marks = [100,200,150,157,....]
I need to assign Grades to those marks using the following key
<25=0, <75=1, <125=2, <250=3, <500=4, >500=5
If Marks < 25, then Grade = 0, if marks < 75 then grade = 1.
I can sort the results and find the first record that matches using Ruby's find function. Is it the best method ? Or is there a way by which I can prepare a range using the key by adding Lower Limit and Upper Limit columns to the table and by populating those ranges using the key? Marks can have decimals too Ex: 99.99
Without using Rails, you could do it like this:
marks = [100, 200, 150, 157, 692, 12]
marks_to_grade = { 25=>0, 75=>1, 125=>2, 250=>3, 500=>4, Float::INFINITY=>5 }
Hash[marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }]
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
With Ruby 2.1, you could write this:
marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }.to_h
Here's what's happening:
Enumerable#map (a.k.a collect) converts each mark m to an array [m, g], where g is the grade computed for that mark. For example, when map passes the first element of marks into its block, we have:
m = 100
a = marks_to_grade.find { |k,_| m <= k }
#=> marks_to_grade.find { |k,_| 100 <= k }
#=> [125, 2]
a.last
#=> 2
so the mark 100 is mapped to [100, 2]. (I've replaced the block variable for the value of the key-value pair with the placeholder _ to draw attention to the fact that the value is not being used in the calculation within the block. One could also use, say, _v as the placeholder.) The remaining marks are similarly mapped, resulting in:
b = marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }
#=> [[100, 2], [200, 3], [150, 3], [157, 3], [692, 5], [12, 0]]
Lastly
Hash[b]
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
or, for Ruby 2.1+
b.to_h
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
You can make use of update_all:
Student.where(:mark => 0...25).update_all(grade: 0)
Student.where(:mark => 25...75).update_all(grade: 1)
Student.where(:mark => 75...125).update_all(grade: 2)
Student.where(:mark => 125...250).update_all(grade: 3)
Student.where(:mark => 250...500).update_all(grade: 4)
Student.where("mark > ?", 500).update_all(grade: 5)
I know this is very silly question. But, I'm very eager to know how to swap the elements in a single line.
Ex:
a, b = 1, 2
I need the answer like this
a, b = 2, 1
a,b = b,a # does work....
irb(main):017:0* a, b = 1, 2
=> [1, 2]
irb(main):018:0> a
=> 1
irb(main):019:0> b
=> 2
irb(main):020:0> a, b = b,a
=> [2, 1]
irb(main):021:0> a
=> 2
irb(main):022:0> b
=> 1
irb(main):023:0>
You say you want to swap an array in your title, yet not in your example. I'm going with the title, so...
x = [1,2,3,4,5]
x.reverse!
=> [5,4,3,2,1]
You could also do this... I guess...
a, b = 1, 2
a, b = b, a
I think you can do
array[0, 1] = array[1, 0]