Mongoid : How to query for array whose type of element is Integer by String? - mongoid

For example: I have Order record: {id: 1, user_ids: [1, 2]}
When the query condition is String:
Order.in(user_ids: ["1"]).count #=> 0
When the query condition is Integer:
Order.in(user_ids: [1]).count #=> 1
But I want to:
Order.in(user_ids: ["1"]).count #=> 1
Because the user_ids arrays contain a mix of numbers and strings.
How do I do this?

Related

Select filed from Array of Hashes in Ruby

So i have this Array of Hashes
{"id"=>50823, "code"=>"1PLAK", "name"=>"Eselente", "order"=>1}
{"id"=>74327, "code"=>"1MAGP", "name"=>"Mango", "order"=>2}
{"id"=>50366, "code"=>"1ANGC", "name"=>"Tabnie", "order"=>3}
{"id"=>76274, "code"=>"1FABD", "name"=>"Slamtab", "order"=>4}
And i want to select the field order (just one field at the same time) for comparing afterwards.
What's the correct way to do it?
Thanks!
When you only want to extract the values of the orders then I would do this:
array = [
{"id"=>50823, "code"=>"1PLAK", "name"=>"Eselente", "order"=>1},
{"id"=>74327, "code"=>"1MAGP", "name"=>"Mango", "order"=>2},
{"id"=>50366, "code"=>"1ANGC", "name"=>"Tabnie", "order"=>3},
{"id"=>76274, "code"=>"1FABD", "name"=>"Slamtab", "order"=>4},
]
array.map { |hash| hash["order"] }
#=> [1, 2, 3, 4]
When you are only interested in the very first value (1 like you wrote in the comments above) then you can do:
array.first["order"] # or array[0]["order"]
#=> 1

How to find the key of a hash whose value has the most elements

I'm using Ruby 2.4.
I have a hash whose key is a number and whose value is an array of elements. How do I find the key in the hash with the value that has the most elements? I know that if my value were a single number I could do this:
my_hash.max_by { |k, v| v }
But since the value is an array, I'm not sure how to tell the above to use the number of elements in the array as what should be maxed.
max_by is the correct method :
my_hash = { a: [1, 2], b: [1, 2, 3], c: [5] }
key, longest_array = my_hash.max_by{ |k, array| array.size }
p key
#=> :b
p longest_array
#=> [1, 2, 3]
You just need to specify on which object the comparison should be. In this case, the size of the array value.
You might need to add some checks first : this will only work if all the hash values respond to :size.
You can do it like this:
my_hash.map {|k, v| [k, v.count]}.max_by {|k, v| v}.first
The first map will return an array of two elements arrays. For each of them, the first element is the key and the second is the number of elements of the corresponding array. Then it uses max_by to return the two element array with the maximum number of elements. Finally, the first method returns the key.
I'm not sure if I understood your question correctly, but I'm assuming you have something like this:
my_hash = {1=>[2, 1, 3, 4], 2=>[1, 2], 3=>[1, 4, 6]}
If that's the case, you can get the key for the largest array like this:
my_hash.max_by{|k,v| v.count}.first

How do I compare integer indexes in arrays when there are duplicate values?

First, some necessary background. I'm trying to make a number-based version of the game Mastermind as a way of learning to code in Ruby. My code basically works like this:
The computer generates an array (#computer_sequence) of 4 random numbers from 1-5
The user enters a 4 digit sequence, which winds up in an array called #user_array.
A method, called compare, iterates through #user_array, comparing the value and index of each number to those in #computer_sequence. The program then tells the user how many of their numbers have the correct value and the correct position, or how many numbers have the correct value only.
The problem: If there are multiple instances of a number in an array, they get the same index, right? Like if I have the array [1, 3, 3, 4], the number three has an index of 1, even though there are two 3s. For this program to work, though, each number has to have a unique position (is index even the word I want here?) in the array, even if the number occurs multiple times. Does that make sense?
Also, here's the code for the compare method:
def compare
value_only = 0
value_and_place = 0
puts "The computer's values are: #{#computer_sequence}"
puts "The user's values are: #{#user_array}"
#user_array.each do |candidate|
#computer_sequence.each do |computer_number|
if candidate == computer_number && #user_array.index(candidate) == #computer_sequence.index(computer_number)
value_and_place +=1
elsif candidate == computer_number && #user_array.index(candidate) != #computer_sequence.index(computer_number)
value_only +=1
end
end
end
Suppose
n = 4
computer = Array.new(n) { [1,2,3,4,5].sample }
#=> [3, 2, 3, 3]
user_digits = [2, 4, 2, 3]
First compute pairs of elements at the same index of computer and user_digits.
pairs = computer.zip(user_digits)
#=> [[3, 2], [2, 4], [3, 2], [3, 3]]
Compute number of values that match at the same position
pairs.count { |c,u| c==u }
#=> 1
Compute number of values that match at different positions
First remove the matches at the same positions of computer and user_digits.
comp, users = pairs.reject { |c,u| c==u }.transpose
#=> [[3, 2, 3], [2, 4, 2]]
meaning
comp #=> [3, 2, 3]
users #=> [2, 4, 2]
Now step through users removing the first matching element in comp (if there is one).
users.each do |n|
i = comp.index(n)
comp.delete_at(i) if i
end
So now:
comp #=> [3,3]
meaning that the number of elements that match at different positions is:
users.size-comp.size
#=> 1
Notice that we could alternatively compute the number of values that match at the same position as
n - users.size
For n equal to 4 this doesn’t offer any significant time saving, but it would if we had a problem with the same structure and n were large.
Alternative calculation
After computing
comp, users = pairs.reject { |c,u| c==u }.transpose
we could write
users.size - comp.difference(users).size
#=> 1
where Array#difference is as I defined it in my answer here.
Here
comp.difference(users)
#=> [3,3]
No, equal elements in an array don't have the same index. Maybe you're thinking that because Array#index only returns the index of the first element equal to its argument. But there are many ways to see that other equal elements have their own indexes. For example,
a = [1, 3, 3, 4]
a[1] == 3 # true
a[2] == 3 # also true
Aside from that issue, your algorithm doesn't quite match the rules of Mastermind. If there is one three in the computer's sequence and the player guesses two threes, both in different positions than the three in the computer's sequence, the player should be told that only one element of their sequence matches the computer's sequence in value but not position.
Given the above, plus that I think it would be clearer to calculate the two numbers separately, I'd do it like this:
value_and_place = 4.times { |i| #user_array[i] == #computer_sequence[i] }
value_only = (#user_array & #computer_sequence).length - value_and_place
That's less efficient than the approach you're taking, but CPU efficiency isn't important for 4-element arrays.
You can pass in the index value to your loop for each candidate using the each_with_index method. So when the first 3 is passed in, index will be 1 and when the second 3 is passed in, index will be 2.
The problem with using .index(candidate) is it returns the first index.
Try this:
#user_array.each_with_index do |candidate, index|
#computer_sequence.each do |computer_number|
if candidate == computer_number && candidate == #computer_sequence[index]
value_and_place +=1
elsif candidate == computer_number && candidate != #computer_sequence[index]
value_only +=1
end
end
end

How to change only elements of one array that are missing in another in Ruby?

If I have 2 arrays, for example a = [1, 2, 3, 4, 5] and b = [4, 7, 9, 1]
and I want to use a method to change elements of the first one. For example
a.map {|x| x.to_s}
but I don't want to change elements that are the same as in the array b.
In this case my desire result would be
a = [1, "2", "3", 4, "5"]
1 and 4 are still integers, because the array b has this elements.
So how can I implement this task?
arrays and methods are used just for example to explain what I mean.
you can achieve this with a simple ternary operator inside your map block to either add the respective element as is or as a string depending on whether the second array contains an element with that value:
a.map { |x| (b.include? x) ? x : x.to_s }
a.map!{|e| b.include?(e) ? e : e.to_s}

How to concatenate/flatten an object's VALUES to an array?

I have the following object:
languages:
english: [ 1, 2, 3 ]
german: [ 4, 5, 6 ]
My goal is to get an array of all values of languagesso that the result looks like [ 1, 2, 3, 4, 5, 6 ].
This is what I have tried:
(word for word in value for key, value of languages)
or
(word for word in languages[lang] for lang in Object.keys languages)
Both methods return a two dimensional array the arrays as first dimension and the values as second dimension
Is there a way to get the desired result using a one-liner?
Use the concat() function:
[1, 2, 3].concat [4, 5, 6]
Yes, you can:
[].concat (val for key, val of languages)...
or
Array::concat (val for key, val of languages)...
which are the same.
(val for key, val of languages) here is the array of all languages arrays to concatenate with one another.
... operator is just a shortcut for java-script apply function.
I am not sure why it has to be in one line ... but here you have it in 2 LOC
result = []
result.splice(result.length, 0, languages[key]...) for key of languages

Resources