Create a new array with common string in all arrays in ruby - arrays

Lets say we have below array
arrays=[["a","b", "c"],["b","g","c"],["b","c","g"]]
To find common array fields we can do arrays[0] & arrays[1] & arrays[2] which will return ["b","c"] in this case. This works fine. But how can we do the same when the array count is not predictable?
My initial thought is doing something like a loop like this.
array_count.times do |index|
#but this way how can I achieve same above or any better approach???
end
Thank you.

Use Reduce method
result=arrays.reduce do |x,y|
x & y
end
p result
output
["b", "c"]
Update
Another short way would be
arrays.reduce(:&)

Related

assigning values of array into variables in ruby

I am trying to assign the each value of an array into different variables. But I am getting error as "Expecting end."
strs = ["flower","flow","flight"]
strs.each_with_index do |x, i|
"b#{i}" = x
end
what is going wrong here ?
What you're doing is like saying
"howdy" = 1
You can't say that. That expression attempts to assign into a string literal. You can't do that. A string literal is not an lvalue (a thing that can go on the left side of an equal sign).
If you are trying to say "make a variable called howdy and assign this value to it", you can't do that because local variables cannot be created on the fly. See How to dynamically create a local variable?.
However, the real core of your issue is that you should not even want to do what you're doing. You already have an array, a wonderful thing that allows you to reference each entry by number. "flower" in your code is already strs[0], and so on. The whole point of the array is that it lets you do that. There is thus no need for individual variables with number names; the array is the variable with number names.
I'm not sure as to WHY you are attempting to create local variables when you already sort of have them. Consider the following where we simply replace your suggested b0 with b[0]:
strs = ["flower","flow","flight"]
b = strs
b[0] #=> "flower"
You could optionally convert your array to a hash using an approach similar to what you've already tried and replace b0 with b[0]:
b = {}
strs.each_with_index do |x, i|
b[i] = x
end
b[0] #=> "flower"
Or even something like this:
my_variables = {}
strs.each_with_index do |x, i|
my_variables[:"b#{i}"] = x
end
my_variables[:b0] #=> "flower"

Return only the first and last items of an array in Ruby

I need to create a method named first_and_last. It should use one argument, an Array, and return a new Array with only the first and last objects of the argument.
Here is what I have so far, but the method seems to ignore the initial argument and just create a new array.
def first_and_last(a)
arr = Array.new
arr = ["a", "b", "c"]
end
You can use the values_at() method to get the first and last elements in an array like this:
def first_and_last(input_array)
input_array.values_at(0,-1)
end
Depending on what behavior you're looking for, it might not work on arrays with 1 or 0 elements. You can read more about the method here.
You can also use .first for the first element in the array, and .last for the last element in an array.
def first_and_last(arr)
[arr.first, arr.last]
end
p first_and_last([1,2,3,4,5,6])
Or....
def first_and_last(arr)
[arr[0], arr[-1]]
end
p first_and_last([1,2,3,4,5,6])

Ruby array intersection returning a blank array

I'm very new to Ruby so please go easy on me. I have this small function that doesn't want to perform an intersection command. If I go into irb and enter the arrays then set the intersection command like: third_array = array1 & array2, third_array returns the common element. But when I run this snippet through irb, it just returns [ ]. Any suggestions?
class String
define_method(:antigrams) do |word2|
array1 = []
array2 = []
array1.push(self.split(""))
array2.push(word2.split(""))
third_array = array1 & array2
third_array
end
end
After looking at what you have, I think your code boils down to this:
class String
def antigrams(word)
self.chars & word.chars
end
end
"flurry".antigrams("flagrant")
# => ["f", "l", "r"]
If you're calling split('') on a word that's effectively the same as chars, though a lot less efficient. Another mistake was pushing a whole array into an array, which creates a nested array of the form [ [ 'f', 'l', ... ] ]. Since the two resulting array-of-arrays have nothing in common, their inner arrays are different, the & operation returns an empty array.
What you meant was to concatenate the one array to the other, something that can be done with += for example.
Whenever you're curious what's happening, either use irb to try out chunks of code, or p to debug at different points in your method.

Comparing a hashes value against array in order

If I have the following hash and array
hash = {'i' => 'i', 'av' => 'av', 'deviceName' => 'Genymotionvbox86p'}
array = ['i', 'av', 'Genymotionvbox86p']
How could I compare that each item in the array matches the hashes value in the same order
So far I have
array.each do |value|
hash.each do |k, v|
expect(v).to eq(value), "expected #{k} to equal #{v}, instead got #{value}"
end
end
This is failing as I get
expected av to equal av, instead got i (RSpec::Expectations::ExpectationNotMetError)
I'm not quite there yet and imagine that a loop within a loop is not the best thing to do either?
I would like to know how to efficiently approach this.
The reason this fails is because you compare every array value with every hash value. To solve this, you can take advantage of the fact that two arrays arrays are equal if all their values in order are equal:
expect(array).to eq hash.values
If you would really want to compare item-by-item, you rightfully noticed that a loop within a loop is not the way to go. You need a single loop to iterate both structures.
For that, you can, for example, use zip, to combine hash and array:
hash.zip(array).each do |(hash_key, hash_value), array_item|
expect(hash_value).to eq array_item
end
or you can resort to using an index:
hash.each_with_index do |(k, v), i|
expect(v).to eq array[i]
end
How could I compare that each item in the array matches the hashes
value in the same order
how about this?
> array == hash.values
#=> true
> array = ["i", "Genymotionvbox86p", "av"] # change the order
> array == hash.values
#=> false

Modify hashes in an array based on another array

I have two arrays like this:
a = [{'one'=>1, 'two'=>2},{'uno'=>1, 'dos'=>2}]
b = ['english', 'spanish']
I need to add a key-value pair to each hash in a to get this:
a = [{'one'=>1, 'two'=>2, 'language'=>'english'},{'uno'=>1, 'dos'=>2, 'language'=>'spanish'}]
I attempted this:
(0..a.length).each {|c| a[c]['language']=b[c]}
and it does not work. With this:
a[1]['language']=b[1]
(0..a.length).each {|c| puts c}
an error is shown:
NoMethodError (undefined method '[]=' for nil:NilClass)
How can I fix this?
a.zip(b){|h, v| h["language"] = v}
a # => [
# {"one"=>1, "two"=>2, "language"=>"english"},
# {"uno"=>1, "dos"=>2, "language"=>"spanish"}
# ]
When the each iterator over your Range reaches the last element (i.e. a.length), you will attempt to access a nonexisting element of a.
In your example, a.length is 2, so on the last iteration of your each, you will attempt to access a[2], which doesn't exist. (a only contains 2 elements wich indices 0 and 1.) a[2] evaluates to nil, so you will now attempt to call nil['language']=b[2], which is syntactic sugar for nil.[]=('language', b[2]), and since nil doesn't have a []= method, you get a NoMethodError.
The immediate fix is to not iterate off the end of a, by using an exclusive Range:
(0...a.length).each {|c| a[c]['language'] = b[c] }
By the way, the code you posted:
(0..a.length).each {|c| puts c }
should clearly have shown you that you iterate till 2 instead of 1.
That's only the immediate fix, however. The real fix is to simply never iterate over a datastructure manually. That's what iterators are for.
Something like this, where Ruby will keep track of the index for you:
a.each_with_index do |hsh, i| hsh['language'] = b[i] end
Or, without fiddling with indices at all:
a.zip(b.zip(['language'].cycle).map(&:reverse).map(&Array.method(:[])).map(&:to_h)).map {|x, y| x.merge!(y) }
[Note: this last one doesn't mutate the original Arrays and Hashes unlike the other ones.]
The problem you're having is that your (0..a.length) is inclusive. a.length = 2 so you want to modify it to be 0...a.length which is exclusive.
On a side note, you could use Array#each_with_index like this so you don't have to worry about the length and so on.
a.each_with_index do |hash, index|
hash['language'] = b[index]
end
Here is another method you could use
b.each_with_index.with_object(a) do |(lang,i),obj|
obj[i]["language"] = lang
obj
end
#=>[
{"one"=>1, "two"=>2, "language"=>"english"},
{"uno"=>1, "dos"=>2, "language"=>"spanish"}
]
What this does is creates an Enumerator for b with [element,index] then it calls with_object using a as the object. It then iterates over the Enumerator passing in each language and its index along with the a object. It then uses the index from b to find the proper index in a and adds a language key to the hash that is equal to the language.
Please know this is a destructive method where the objects in a will mutate during the process. You could make it non destructive using with_object(a.map(&:dup)) this will dup the hashes in a and the originals will remain untouched.
All that being said I think YAML would be better suited for a task like this but I am not sure what your constraints are. As an example:
yml = <<YML
-
one: 1
two: 2
language: "english"
-
uno: 1
dos: 2
language: "spanish"
YML
require 'yaml'
YAML.load(yml)
#=>[
{"one"=>1, "two"=>2, "language"=>"english"},
{"uno"=>1, "dos"=>2, "language"=>"spanish"}
]
Although using YAML I would change the structure for numbers to be more like language => Array of numbers by index e.g. {"english" => ["zero","one","two"]}. That way you can can access them like ["english"][0] #=> "zero"

Resources