What's a simple way to write a method for checking if a certain number of array elements match. For example
["dog", "cat", "dog", "dog"].has_matching(3)
# true
and
["dog", "cat", "dog", "cat"].has_matching(3)
# false
Ideally the class of the objects being compared would not matter.
You could add a method to Array:
class Array
def check_if_minimum_duplicates(min_dup)
group_by{|el| el }.any?{|k, v| v.count >= min_dup }
end
end
and use it like this:
irb(main):006:0> puts ["dog", "cat", "dog", "dog"].check_if_minimum_duplicates(3)
true
=> nil
irb(main):007:0> puts ["dog", "cat", "dog", "cat"].check_if_minimum_duplicates(4)
false
=> nil
Related
I have many hashes that contain the same keys but are all out of order from each other. I need to reorder each one to make them fit the proper order:
correct_order = {
:cat => "cat",
:dog => "dog",
:bear => "bear",
:zebra => "zebra",
:monkey => "monkey"
}
hash1 = {
:bear => "bear",
:cat => "cat"
}
hash2 = {
:cat => "cat",
:monkey => "monkey",
:zebra => "zebra",
:bear => "bear"
}
hash3 = {
:dog => "dog",
:monkey => "monkey",
:cat => "cat"
}
How would I go about comparing the key order of hash1, hash2, and hash3 to make each key match its place in the correct_order hash? hash2 would become:
hash2 {
:cat => "cat",
:bear => "bear",
:zebra => "zebra",
:monkey => "monkey"
}
Note that each new hash will not necessarily have all the keys. Each hash will be of varing size AND varying order.
Thank you
You can't "sort" a hash.
The doc says: Hashes enumerate their values in the order that the corresponding keys were inserted.
So you will have to create a new hash. For instance:
def sort_hash(ordered_keys, hash_to_sort)
new_hash= {}
ordered_keys.each do |key|
new_hash[key]= hash_to_sort[key] if hash_to_sort.has_key?(key)
end
new_hash
end
correct_order = {
:cat => "cat",
:dog => "dog",
:bear => "bear",
:zebra => "zebra",
:monkey => "monkey"
}
hash2 = {
:cat => "cat",
:monkey => "monkey",
:zebra => "zebra",
:bear => "bear"
}
puts sort_hash(correct_order.keys, hash2)
Note that the function returns a new hash. hash2 is not modified.
There is something that needs be said about the order of hash elements, but I'll address that after showing you how you can do what you want to do. This assumes you are using Ruby 1.9+, where the order of key insertion is maintained.
Code
def reorder(hash, order)
keys = order & hash.keys
Hash[keys.zip(hash.values_at(*keys))]
end
Examples
order = correct_order.keys
#=> [:cat, :dog, :bear, :zebra, :monkey]
reorder(hash1, order)
#=> {:cat=>"cat", :bear=>"bear"}
reorder(hash2, order)
#=> {:cat=>"cat", :bear=>"bear", :zebra=>"zebra", :monkey=>"monkey"}
reorder(hash3, order)
#=> {:cat=>"cat", :dog=>"dog", :monkey=>"monkey"}
Explanation
Let's see how the code deals with hash1:
hash = hash1
#=> {:bear=>"bear", :cat=>"cat"}
order
#=> [:cat, :dog, :bear, :zebra, :monkey]
keys = order & hash.keys
#=> [:cat, :dog, :bear, :zebra, :monkey] & [:bear, :cat]
#=> [:cat, :bear]
arr = keys.zip(hash.values_at(*keys))
#=> keys.zip({:bear=>"bear", :cat=>"cat"}.values_at(*[:cat, :bear]))
#=> keys.zip({:bear=>"bear", :cat=>"cat"}.values_at(:cat, :bear))
#=> keys.zip(["cat", "bear"])
#=> [[:cat, "cat"], [:bear, "bear"]]
Hash[arr]
#=> {:cat=>"cat", :bear=>"bear"}
In Ruby 1.9+ you can instead write the last step as:
arr.to_h
Note that the method Array#& preserves the order of the first array.
Hash order
Prior to Ruby 1.9 you could not depend on the order of key-value pairs (let's just say keys) in a hash. That changed in 1.9. Now you can be confident that when you enumerate the keys of a hash, the order of the enumeration will follow the order in which the keys were added to the hash. Be warned that that change was not embraced by all Rubyists. If you've been thinking of hashes as unordered collections for twenty years, it's a big change. Personally, I like the change and have found good uses for it.
Simple ruby question. Lets say I have an array of 10 strings and I want to move elements at array[3] and array[5] into a totally new array. The new array would then only have the two elements I moved from the first array, AND the first array would then only have 8 elements since two of them have been moved out.
Use Array#slice! to remove the elements from the first array, and append them to the second array with Array#<<:
arr1 = ['Foo', 'Bar', 'Baz', 'Qux']
arr2 = []
arr2 << arr1.slice!(1)
arr2 << arr1.slice!(2)
puts arr1.inspect
puts arr2.inspect
Output:
["Foo", "Baz"]
["Bar", "Qux"]
Depending on your exact situation, you may find other methods on array to be even more useful, such as Enumerable#partition:
arr = ['Foo', 'Bar', 'Baz', 'Qux']
starts_with_b, does_not_start_with_b = arr.partition{|word| word[0] == 'B'}
puts starts_with_b.inspect
puts does_not_start_with_b.inspect
Output:
["Bar", "Baz"]
["Foo", "Qux"]
a = (0..9).map { |i| "el##{i}" }
x = [3, 5].sort_by { |i| -i }.map { |i| a.delete_at(i) }
puts x.inspect
# => ["el#5", "el#3"]
puts a.inspect
# => ["el#0", "el#1", "el#2", "el#4", "el#6", "el#7", "el#8", "el#9"]
As noted in comments, there is some magic to make indices stay in place. This can be avoided by first getting all the desired elements using a.values_at(*indices), then deleting them as above.
Code:
arr = ["null","one","two","three","four","five","six","seven","eight","nine"]
p "Array: #{arr}"
third_el = arr.delete_at(3)
fifth_el = arr.delete_at(4)
first_arr = arr
p "First array: #{first_arr}"
concat_el = third_el + "," + fifth_el
second_arr = concat_el.split(",")
p "Second array: #{second_arr}"
Output:
c:\temp>C:\case.rb
"Array: [\"null\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"s
even\", \"eight\", \"nine\"]"
"First array: [\"null\", \"one\", \"two\", \"four\", \"six\", \"seven\", \"eight
\", \"nine\"]"
"Second array: [\"three\", \"five\"]"
Why not start deleting from the highest index.
arr = ['Foo', 'Bar', 'Baz', 'Qux']
index_array = [2, 1]
new_ary = index_array.map { |index| arr.delete_at(index) }
new_ary # => ["Baz", "Bar"]
arr # => ["Foo", "Qux"]
Here's one way:
vals = arr.values_at *pulls
arr = arr.values_at *([*(0...arr.size)] - pulls)
Try it.
arr = %w[Now is the time for all Rubyists to code]
pulls = [3,5]
vals = arr.values_at *pulls
#=> ["time", "all"]
arr = arr.values_at *([*(0...arr.size)] - pulls)
#=> ["Now", "is", "the", "for", "Rubyists", "to", "code"]
arr = %w[Now is the time for all Rubyists to code]
pulls = [5,3]
vals = arr.values_at *pulls
#=> ["all", "time"]
arr = arr.values_at *([*(0...arr.size)] - pulls)
#=> ["Now", "is", "the", "for", "Rubyists", "to", "code"]
I am comparing large arrays to find missing elements. But one array will all be capitalized and the other is not. So I want to format the one array but am having issues. This is an example of the array I am trying to format.
array = [ 023, "Bac001", "abc123"]
Then I try to capitalize everything
array.map!(&:upcase)
but get undefined method 'upcase' for 023
is there a way around this?
I'd use Object#respond_to?:
def upcase_elements(ary)
ary.map { |e| e.respond_to?(:upcase) ? e.upcase : e }
end
upcase_elements([23, "BAC001", "ABC123"])
#=> [23, "BAC001", "ABC123"]
Checking if the receiver responds to a method is more flexible than checking its type:
upcase_elements([:foo, "bar"])
#=> [:FOO, "BAR"]
array.map! { |s| s.kind_of?(String) ? s.upcase : s }
This will not attempt to upcase any non-string element of the array. So it will work on arrays like:
array = [23, 1.27, "Bac001", "abc123", {:foo => 3}]
Yielding:
[23, 1.27, "BAC001", "ABC123", {:foo => 3}]
Here's my array:
num_arr = ["cat","dog","penguin","penguin"]
I want to print each index position's contents unless those contents are the same as the contents of the last index position. In this case, that would be a loop that prints...
["cat","dog","penguin"]
EDIT: I mean I'd like to print everything up to the last element UNLESS the last element is identical to the second-to-last element. Sorry for the confusion.
What about this?
num_arr[0...-1] + (num_arr[-2] == num_arr[-1] ? [] : [num_arr[-1]])
Less verbose, but somewhat more difficult to undestand:
num_arr[0...-2] + num_arr[-2..-1].uniq
#=> ["cat", "dog", "penguin"]
foo = ["cat","dog","penguin","penguin"]
=> ["cat", "dog", "penguin", "penguin"]
answer = foo.reject { |ele| ele == foo.last }
=> ["cat", "dog"]
I believe this is what you're asking for.
new_array = array.each_with_object([]) { |element, result| result << element unless result.last == element }
new_array.each { |element| puts element }
If you just want to print all unique entries use the uniq method of Array in this case.
["cat","dog","penguin","penguin"].uniq.map{|a| p a}
console
ruby-1.9.2-p290 :004 > ["cat","dog","penguin","penguin"].uniq.map{|a| p a}
"cat"
"dog"
"penguin"
=> ["cat", "dog", "penguin"]
Update:
If you want to only do a comparison for the last entry, you could do that by storing the last entry in #prev and assigning it after the comparison to the current entry.
["cat", "dog", "penguin", "penguin"].map{|a| p a if #prev!=a;#prev=a }
console
ruby-1.9.2-p290 :009 > ["cat", "dog", "penguin", "dog", "penguin"].map{|a| p a if #prev!=a;#prev=a }
"cat"
"dog"
"penguin"
"dog"
"penguin"
ruby-1.9.2-p290 :010 > ["cat", "dog", "penguin", "penguin"].map{|a| p a if #prev!=a;#prev=a }
"cat"
"dog"
"penguin"
I would like to display the following:
anu1 cau1 doh1 bef1
To do that I need to complete the following Ruby code by adding only ONE statement.
a = ["ant", "cat", "dog", "bee"]
It sounds like you need to perform a succ function each of the words, which will give you the next value for each of them, then you would just need to append "1" to each.
Example: -Forgive my syntax, Haven't used Ruby in a while-
a.map {|word| word.succ << "1"}
should output:
["anu1", "cau1", "doh1", "bef1"]
a = ["ant", "cat", "dog", "bee"]
# => ["ant", "cat", "dog", "bee"]
a.map {|str| str.succ << "1" }
# => ["anu1", "cau1", "doh1", "bef1"]