I'm looking for a little method that could check if the first n characters of string in the array are the same.
For example:
["Marseille", "Marsan", "Martin"].method(3) => true
["Mar", "Mar", "Mar"]
["Marseille", "Marsan", "Martin"].method(4) => false
["Mars", "Mars", "Mart"]
A shorter version would be using Array#uniq with a block and Enumerable#one?:
class Array
def same_prefix?(n)
uniq{|x| x[0, n]}.one?
end
end
Demonstration
Use Array#map to get prefix array:
["Marseille", "Marsan", "Martin"].map { |x| x[0,4] }
# => ["Mars", "Mars", "Mart"]
and Array#uniq to remove duplicated items.
["Marseille", "Marsan", "Martin"].map { |x| x[0,4] }.uniq
# => ["Mars", "Mart"]
If all prefixes are same, result should be an array of single item.
class Array
def same_prefix?(n)
self.map { |x| x[0, n] }.uniq.size == 1
end
end
["Marseille", "Marsan", "Martin"].same_prefix?(3)
# => true
["Marseille", "Marsan", "Martin"].same_prefix?(4)
# => false
class Array
def same_start?(n)
start = first[0,n]
all? { |e| e[0,n] == start }
end
end
["Marseille", "Marsan", "Martian"].same_prefix?(3) #=> true
["Marseille", "Marsan", "Martian"].same_prefix?(4) #=> false
Here is one more way to do this:
arr = ["Marseille", "Marsan", "Martin"]
n = 3
arr.each_cons(2).all? {|s1, s2| s1[0...n] == s2[0...n]}
#=> true
A slight variant can be:
arr.map{|s| s[0...n]}.each_cons(2).all? {|s1, s2| s1 == s2}
Related
I have a hash of integers as keys and arrays of strings as values. I need to convert this to a new hash that inverts this relationship with each item from the array of strings in the original hash values becoming a key in the new hash and each original key becoming the associated value. For example:
original = {1 => ['a', 'b', 'c'], 2 => ['g', 'm', 'z']}
new_hash = {'a' => 1, 'b' => 1, 'c' => 1, 'g' => 2, 'm' => 2, 'z' => 2}
I'm struggling to extract the items from the original array values. It's easy enough to do
original.each { |k, v| new_hash[v] = k }
but this keeps the original array as the new key. I've tried doing something like
original.each { |k, v| new_hash[v.each { |i| i }] = k }
but this also returns the original array for some reason.
Another one, via Array#product:
original.flat_map { |k, v| v.product([k]) }.to_h
#=> {"a"=>1, "b"=>1, "c"=>1, "g"=>2, "m"=>2, "z"=>2}
original.flat_map { |k, vs| vs.map { |v| {v => k} } }.reduce(&:merge)
the below snippet will give what you want, but let me think on a more readable and elegant solution.
newhash = {}
original.each do |k,v|
v.each do |v2|
newhash[v2] = k
end
end
#=> {1=>["a", "b", "c"], 2=>["g", "m", "z"]}
newhash
#=> {"a"=>1, "b"=>1, "c"=>1, "g"=>2, "m"=>2, "z"=>2}
Your approach is close. You'll have to iterate each element in the values array when assigning the new key/value pair to the newHash
newHash = {}
original.each { |k, v| v.each {|i| newHash[i] = k}}
original.map { |number, ary| Hash[ary.map { |char| [char, number] }] }.reduce(&:merge)
Given:
my_array = ['america', 'bombay', 'bostwana', 'cameroon']
I can locate the index of the first element that, say, begins with 'bo' with
my_array.find_index { |l| l.start_with? 'bo' }
How can I locate all such elements?
If you want the elements:
my_array.find { |element| element.start_with? 'bo' } # => "bombay"
my_array.select { |element| element.start_with? 'bo' } # => ["bombay", "bostwana"]
If you want the indices:
my_array.index { |element| element.start_with? 'bo' } # => 1
my_array.map.with_index.select { |element, _| element.start_with? 'bo' }.map(&:last)
# => [1, 2]
You can use map.with_index with a conditional and compact the result.
my_array.map.with_index{ |element, index| index if element.start_with? 'bo' }.compact
How this works
map
map will take all of the values and "map" them to the value that is returned when each item is passed into the given block.
my_array.map { |element| element.start_with? 'bo' }
# => [false, true, true, false]
with_index
To get the index values you can use with_index like this:
my_array.map.with_index { |element, index| index }
# => [0, 1, 2, 3]
my_array.map.with_index { |element, index| index if element.start_with? 'bo' }
# => [nil, 1, 2, nil]
compact
Then, to get rid of the nil values you can call compact on the returned array.
my_array.map.with_index{ |element, index| index if element.start_with? 'bo' }.compact
# => [1, 2]
['america', 'bombay', 'bostwana', 'cameroon']
.each_with_index # to get indices
.grep(->(e) { e.first.start_with? "bo" }) # I ❤ Enumerable#grep
.map(&:last) # get indices
#⇒ [1, 2]
I posted this to show the approach that is rarely used. Enumerable#grep accepts anything, that is used to select items basing on triple-equal case comparison.
Passing Proc instance there is cool, because Proc has a default Proc#=== implementation: it’s simply being invoked on receiver.
In the interest of readability, I would use the following:
my_array.each_index.select {|idx| my_array[idx].start_with?("bo") }
I have an array of arrays [[1,2,3],[4,5,6],[7,8,9]]. I also have an array of integers [3,4,5,6,8].
Is it possible for me to check if my integers match a complete array in the array of arrays?
So I have 4,5,6 in the int array, and it matches the middle array [4,5,6].
This should work
a = [[1,2,3],[4,5,6],[7,8,9]]
integers = [3,4,5,6,8]
a.any? { |sub_array| sub_array.all? { |item| integers.include? item } }
Try this:
array_1 = [[1,2,3],[4,5,6],[7,8,9]]
array_2 = [3,4,5,6,8]
array_1.any? { |e| (e - array_2).empty? }
# => true
array1 = [[1,2,3],[4,5,6],[7,8,9]]
array2 = [4,5,6]
result = array1.map{|inner_array| inner_array - array2}
# => [[1, 2, 3], [], [7, 8, 9]]
result.any?{|inner_array| inner_array.empty?}
# => true
Assuming you expect a true or false and order doesn't matter, the following works:
require 'set'
a1 = [[1,2,3],[4,5,6],[7,8,9]]
a2 = [3,4,5,6,8]
a1.any? { |item| item.to_set.subset? a2.to_set } #=> true
Assuming you want the index into a1 or nil
a1.index { |item| item.to_set.subset? a2.to_set }
Assuming you want the subset itself or nil
index = a1.index { |item| item.to_set.subset? a2.to_set }
index && a1[index]
I am trying to convert this array:
["dog", 5 , "big house"]
to hash:
{"dog" => 3 , 5 => 25, "big house" => 9}
The value will be the number of characters of the string (key).
If it's an integer (key), then the value will be to the power of 2.
This is what I got so far, but it only converts the string (key):
h = {}
arr.each do |x,y|
y = x.length
h[x] = y
end
▶ arr = ["dog", 5, "big house"]
#⇒ [ "dog", 5, "big house" ]
▶ arr.zip(arr.map do |e|
▷ case e
▷ when String then e.length
▷ when Integer then e ** 2
▷ else raise 'Neither string nor numeric given'
▷ end
▷ end).to_h
#⇒ { "dog" => 3, 5 => 25, "big house" => 9 }
You could use the Enumberable#each_with_object method like so:
array = ["dog",5,"big house"]
array.each_with_object({}) {|x,hash| x.is_a?(String) ? hash[x] = x.length : hash[x] = x**2}
# => {"dog"=>3,5=>25,"big house"=>9}
The each_with_object method is very similar to the inject method, so it'll iterate through the array, then once completed it'll return the newly given object. The difference between each_with_object and inject is that each_with_object is immutable, so you can't do something like:
(1..5).each_with_object(0) {|num,sum| sum += num}
It'll only return a zero.
You can use the Hash[...] constructor to convert an array of [key, value] pairs to a Hash. So here's another option:
arr = ["dog", 5, "big house"]
result = Hash[ arr.map { |e| [e, e.to_i == e ? e**2 : e.length] } ]
# => {"dog"=>3, 5=>25, "big house"=>9}
Since Ruby 2, you can use Array#to_h to do the same thing:
result = arr.map { |e| [e, e.to_i == e ? e**2 : e.length] }.to_h
# => {"dog"=>3, 5=>25, "big house"=>9}
If you have the following array:
arr = ["dog", 5, "big house"]
First you can create a method to convert elements into your desired format based on the element's class:
def convert_to_hash_val(element)
case element
when Fixnum
element**2
else
element.size
end
end
Then create an array of [element, converted_element] pairs and use the Hash#[] function to convert this to key, value pairs:
Hash[ arr.map {|element| [element, convert_to_hash_val(element)]} ]
# => { "dog" => 3, 5 => 25, "big house" => 9 }
You can use inject method.
is_a? method can judge the element is String or Integer.
["dog", 2 , "big house"].inject({}) do |sum, e|
if e.is_a? Integer
sum.merge({e => e*e})
elsif e.is_a? String
sum.merge({e => e.length})
end
end
=> {"dog"=>3, 2=>4, "big house"=>9}
as #Myst said, h[:key] = value has better performance than merge, so you also can do like this:
["dog", 2 , "big house"].inject({}) do |sum, e|
if e.is_a? Integer
sum[e] = e*e
elsif e.is_a? String
sum[e] = e.length
end
sum
end
arr= ["dog", 5, "big house"]
two_dimensional_array = arr.zip(arr.map do |e|
case e
when String then e.length
when Integer then e ** 2
else raise 'Neither string nor numeric given'
end
end)
my_hash = Hash[*two_dimensional_array.flatten]
result_hash = {}
arr = ["dog", 2, "big house"]
arr.each do |el|
if el.is_a?(String)
result_hash[el] = el.length
elsif el.is_a?(Fixnum)
result_hash[el] = el ** 2
end
end
Remember that the each block called on an array only throws one argument into the block, which is the currently iterated element.
From the following array of hashes:
x = [
{"creationDate"=>123456,"createdBy"=>"test_user1"},
{"creationDate"=>123459,"createdBy"=>"test_user1"},
{"creationDate"=>123458,"createdBy"=>"test_user1"},
{"creationDate"=>123454,"createdBy"=>"test_user2"},
{"creationDate"=>123452,"createdBy"=>"test_user2"},
{"creationDate"=>123451,"createdBy"=>"test_user2"}
]
I am trying to find the maximum :creationDate value where :createdBy value is "test_user1". I did this:
x.map {|a| a['creationDate'] if a['createdBy'] == 'test_user1'}
# => [123456,123459,123458,nil,nil,nil]
I want to get rid of the nil so that I can apply max to that array. How do I modify the code above?
What you want to do here is:
x.select { |record| record[:createdBy] == 'test_user1' }.map { |record| record[:creationDate] }.max
# => 123459
In general, to remove nils from an array, you can simply call Array#compact:
[1, nil, nil, 2, 'foo', nil].compact # => [1, 2, "foo"]
It' close to what you plan to do in python:
x.select { |n| n[:createdBy] == "test_user1" }.max_by { |n| n[:creationDate] }
First operation select on records created by "test_user1", while second operation get the maximum of the resulting array based on the creationDate
I'd guess something like :
x.delete_if {|x| x == nil}
This is a good candidate for a map-reduce function such as inject.
x = [{"creationDate" => 123456,"createdBy" => "test_user1"},
{"creationDate" => 123459,"createdBy" => "test_user1"},
{"creationDate" => 123458,"createdBy" => "test_user1"},
{"creationDate" => 123454,"createdBy" => "test_user2"},
{"creationDate" => 123452,"createdBy" => "test_user2"},
{"creationDate" => 123451,"createdBy" => "test_user2"}]
x.inject(nil) do |result, item|
if item["createdBy"] == "test_user1" && (result.nil? or item["creationDate"] > result)
item["creationDate"]
else
result
end
end
Here's a possible implementation of the inject. The block will iterate each item in the collection and will always maintain the highest value.
x.min_by{|h| [h[:createdBy], -h[:creationDate]]}[:creationDate]
# => 123459
[123456, 123459, 123458, nil, nil, nil].compact
# => [123456, 123459, 123458]
This works:
x.map {|a| a['creationDate'] if a['createdBy'] == 'test_user1'}.compact.max
# => 123459