All indexes having the same character in nested array Ruby - ruby

I'm trying to see if an index, say index 3, all has the same character. For example:
nested_array = [[47, 44, 71, 'x', 88],
[22, 69, 75, 'x', 73],
[83, 85, 97, 'x', 57],
[25, 31, 96, 'x', 51],
[75, 70, 54, 'x', 83]]
As we can see, index 3 has all x'x in it instead of numbers. I want to be able to verify if ALL of the indexes have an x, if so, program returns true. If all but 1 have x's it would return false.
I have now implemented this conditional statement as it iterates through each index looking for a value the size equal to 1. This seemed to do the trick.
if array.map {|zero| zero[0]}.uniq.size == 1
return true
elsif array.map {|one| one[1]}.uniq.size == 1
return true
elsif array.map {|two| two[2]}.uniq.size == 1
return true
elsif array.map {|three| three[3]}.uniq.size == 1
return true
elsif array.map {|four| four[4]}.uniq.size == 1
return true
else
return false
end

nested_array.empty? || nested_array.map {|row| row[3]}.uniq.one?
The special condition for empty arrays is necessary because they trivially fulfill any condition that is specified for each element, but the uniqueness test would not pick it up. Feel free to drop it if you will not in fact have an empty array, or the test should fail if the array is empty.
EDIT: Seems I misunderstood the question - try this as an alternative to your multi-if:
nested_array.transpose.any? { |column| column.uniq.one? }
"When you flip rows and columns, does any of the rows (that used to be columns) have only one unique element?"
(if you want true for [], again, prefix with nested_array.empty? || ...).
EDIT: Thanks to spickermann, replaced .size == 1 with .one?. It really does read better.

You did not specify that all elements of nested_array are necessarily of the same size, so I will offer a solution does not have that as a requirement:
nested_array = [[47, 44, 71, 'x', 88],
[75, 70, 54, 'x', 83, 85, 90],
[22, 69, 75, 'x', 73],
[83, 85, 97, 'x', 57],
[25, 31, 96, 'x', 51, 33]]
nested_array.first.zip(*nested_array[1..-1]).any? {|row| row.uniq.size==1}
#=> true
We have:
b = nested_array.first.zip(*nested_array[1..-1])
#=> [[47, 75, 22, 83, 25],
# [44, 70, 69, 85, 31],
# [71, 54, 75, 97, 96],
# ["x", "x", "x", "x", "x"],
# [88, 83, 73, 57, 51]]
b.any? { |row| row.uniq.size == 1 }
#=> true
Initally, I had:
nested_array.first.zip(*nested_array[1..-1]).any? {|row|
row[1..-1].all? { |e| e == row.first } }
rather than { |row| row.uniq.size==1 }. As #Amadan points out, the former should be a bit faster.
If the question is whether a specific element of b contains all the same values:
index = 3
a = nested_array.first.zip(*nested_array[1..-1])
(index >= a.size) ? false : a[index].uniq.size==1
#=> true

Related

How to delete elements from an array that are followed by the same elements

I have an array. There are two 46s at indices 3 and 7:
arr = [7, 68, 42, 46, 9, 91, 77, 46, 86, 1]
I got a mission to sort this array using selection sort, but not .sort. The result should be:
[1, 7, 9, 42, 46, 46, 68, 77, 86, 91]
So, I did this in a .rb file:
def insertion_sort(arr)
length = arr.size
arr.each_with_index do |number, index|
puts "Now the index is #{index}"
current_minimum = arr.last(length - index).min
puts "Now the current_minimum in last#{length - index} elements is #{current_minimum}"
arr.delete(current_minimum)
arr.insert(index, current_minimum)
end
end
arr = [7, 68, 42, 46, 9, 91, 77, 46, 86, 1]
answer = insertion_sort(arr)
puts answer.to_s
I executed this file, then got this:
[1, 7, 9, 42, 68, 91, 77, 86, 46]
If I delete one 46, it came out with this:
[1, 7, 9, 42, 46, 68, 77, 86, 91]
My code does not work when there are multiple occurrences of some single value in the array. When the each_with_index block went to index 3, it deleted all 46s from the rest of the array.
Could anyone tell me how to correct this?
To "emulate" this kind of selection sort you could try iterating over an array using a range starting from 0 as the value with index 0 and with the length of the array as last element. This range won't take the last array value.
Using each you get access to each value from the created range, then, using that "index", you can create a new range, again without taking the last element but this time one step ahead, that's adding 1 to the current value for a. This way again, using each you have access to a and b, from the "parent" and "child" range created before.
Now you can check if the value for the element with index b in the array is minor than the value for the element with index a in the array, if this validation is true then create a temp variable with the value of the element in the array with index b, then the element in the array at position (index) b will be equal to the element in the array with the position a, finally the element in the array at position a, will be equal to the temp variable created before.
Finally return the array passed as argument.
def insertion_sort(array)
(0...array.length).each do |a|
((a+1)...array.size).each do |b|
if array[b] < array[a]
temp = array[b]
array[b] = array[a]
array[a] = temp
end
end
end
array
end
arr = [7, 68, 42, 46, 9, 91, 77, 46, 86, 1]
p insertion_sort(arr)
# => [1, 7, 9, 42, 46, 46, 68, 77, 86, 91]
As added #MarkThomas, you can "skip" the temp variable by swapping the array values with a and b indexes:
def insertion_sort(array)
(0...array.length).map do |a|
((a+1)...array.size).each do |b|
array[a], array[b] = array[b], array[a] if array[b] < array[a]
end
end
array
end
Thanks all of you. I improved my code and it seems work well.Here's the code:
def insertion_sort(arr)
length = arr.size
arr.each_with_index do |number, index|
current_minimum = arr.last(length - index).min
current_minimum_index = arr.last(length-index).index(current_minimum) + index # this insure it will delete the right element
arr.delete_at(current_minimum_index)
arr.insert(index, current_minimum)
end
end
arr = [7, 68, 42, 46, 9, 91, 77, 46, 86, 1]
answer = insertion_sort(arr)
puts "---------------------------------------------------------------"
puts "Finally we get #{answer.to_s}"
To implement a Selection Sort in Ruby you can use Kernel#loop, being careful to pass obj to break in order to get the correct return value.
arr = [7, 68, 42, 46, 9, 91, 77, 46, 86, 1]
sorted = loop.with_object([]) do |_,obj|
mindex = arr.index arr.min #find index of a minimum
obj << arr.delete_at(mindex) #push this minimum value to obj
break obj if arr.empty?
end
sorted #=> [1, 7, 9, 42, 46, 46, 68, 77, 86, 91]
See with_object for more info.

make bingo board arrays appear in neat rows as output ruby [duplicate]

This question already has an answer here:
replacing an element in nested array ruby
(1 answer)
Closed 7 years ago.
Hello I have my following bingo code that marks X for numbers that get returned:
class BingoBoard
def initialize(board)
#bingo_board = board
end
def number_letter
#letter = ['B','I','N','G','O'].sample
#number = rand(1..100)
end
def checker
number_letter
#bingo_board.map! do |n|
if n.include?(#number) #cleaned up code from the initial solution.
n.map! { |x| x == #number ? 'X' : x}
else
n
end
end
end
end
My question is how do I change my code so that when I'm using the test code:
board = [[47, 44, 71, 8, 88],
[22, 69, 75, 65, 73],
[83, 85, 97, 89, 57],
[25, 31, 96, 68, 51],
[75, 70, 54, 80, 83]]
new_game = BingoBoard.new(board)
new_game.checker
It will appear neatly like a bingo board in irb.
Right now it looks like:
=>[[47, 44, 71, 8, 88], [22, 69, 75, 65, 73], ["X", 85, 97, 89, 57], [25, 31, 96, 68, 51], [75, 70, 54, 80, "X"]]
Append .map { |block| puts block.inspect } to the new_game.checker call.

Finding similar objects located in same index position of arrays in Ruby

I have the following hash:
hash = {"1"=>[ 5, 13, "B", 4, 10],
"2"=>[27, 19, "B", 18, 20],
"3"=>[45, 41, "B", 44, 31],
"4"=>[48, 51, "B", 58, 52],
"5"=>[70, 69, "B", 74, 73]}
Here is my code:
if hash.values.all? { |array| array[0] == "B" } ||
hash.values.all? { |array| array[1] == "B" } ||
hash.values.all? { |array| array[2] == "B" } ||
hash.values.all? { |array| array[3] == "B" } ||
hash.values.all? { |array| array[4] == "B" }
puts "Hello World"
What my code does is iterates through an array such that if the same element appears in the same index position of each array, it will output the string "Hello World" (Since "B" is in the [2] position of each array, it will puts the string. Is there a way to condense my current code without having a bunch of or's connecting each index of the array?
Assuming all arrays are always of the same length, the following gives you the column indexes where all values are equal:
hash.values.transpose.each_with_index.map do |column, index|
index if column.all? {|x| x == column[0] }
end.compact
The result is [2] for your hash. So you know that for all arrays the index 2 has the same values.
You can print "Hello World" if the resulting array has at least one element.
How does it work?
hash.values.transpose gives you all the arrays, but with transposed (all rows are now columns) values:
hash.values.transpose
=> [[5, 27, 45, 48, 70],
[13, 19, 41, 51, 69],
["B", "B", "B", "B", "B"],
[4, 18, 44, 58, 74],
[10, 20, 31, 52, 73]]
.each_with_index.map goes over every row of the transposed array while providing an inner array and its index.
We look at every inner array, yielding the column index only if all elements are equal using all?.
hash.values.transpose.each_with_index.map {|column, index| index if column.all? {|x| x == column[0] }
=> [nil, nil, 2, nil, nil]
Finally, we compact the result to get rid of the nil values.
Edit: First, I used reduce to find the column with identical elements. #Nimir pointed out, that I re-implemented all?. So I edited my anwer to use all?.
From #tessi brilliant answer i though of this way:
hash.values.transpose.each_with_index do |column, index|
puts "Index:#{index} Repeated value:#{column.first}" if column.all? {|x| x == column[0]}
end
#> Index:2 Repeated value:B
How?
Well, the transpose already solves the problem:
hash.values.transpose
=> [[5, 27, 45, 48, 70],
[13, 19, 41, 51, 69],
["B", "B", "B", "B", "B"],
[4, 18, 44, 58, 74],
[10, 20, 31, 52, 73]
]
We can do:
column.all? {|x| x == column[0]}
To find column with identical items
Assuming that all the values of the hash will be arrays of the same size, how about something like:
hash
=> {"1"=>[5, 13, "B", 4, 10], "2"=>[27, 19, "B", 18, 20], "3"=>[45, 41, "B", 44, 31], "4"=>[48, 51, "B", 58, 52], "5"=>[70, 69, "B", 74, 73]}
arr_of_arrs = hash.values
=> [[5, 13, "B", 4, 10], [27, 19, "B", 18, 20], [45, 41, "B", 44, 31], [48, 51, "B", 58, 52], [70, 69, "B", 74, 73]]
first_array = arr_of_arrs.shift
=> [5, 13, "B", 4, 10]
first_array.each_with_index do |element, index|
arr_of_arrs.map {|arr| arr[index] == element }.all?
end.any?
=> true
This is not really different from what you have now, as far as performance - in fact, it may be a bit slower. However, it allows for a dynamic number of incoming key/value pairs.
I ended up using the following:
fivebs = ["B","B","B","B","B"]
if hash.values.transpose.any? {|array| array == fivebs}
puts "Hello World"
If efficiency, rather than readability, is most important, I expect this decidedly un-Ruby-like and uninteresting solution probably would do well:
arr = hash.values
arr.first.size.times.any? { |i| arr.all? { |e| e[i] == ?B } }
#=> true
Only one intermediate array (arr) is constructed (e.g, no transposed array), and it quits if and when a match is found.
More Ruby-like is the solution I mentioned in a comment on your question:
hash.values.transpose.any? { |arr| arr.all? { |e| e == ?B } }
As you asked for an explanation of #Phrogz's solution to the earlier question, which is similar to this one, let me explain the above line of code, by stepping through it:
a = hash.values
#=> [[ 5, 13, "B", 4, 10],
# [27, 19, "B", 18, 20],
# [45, 41, "B", 44, 31],
# [48, 51, "B", 58, 52],
# [70, 69, "B", 74, 73]]
b = a.transpose
#=> [[ 5, 27, 45, 48, 70],
# [ 13, 19, 41, 51, 69],
# ["B", "B", "B", "B", "B"],
# [ 4, 18, 44, 58, 74],
# [ 10, 20, 31, 52, 73]]
In the last step:
b.any? { |arr| arr.all? { |e| e == ?B } }
#=> true
(where ?B is shorthand for the one-character string "B") an enumerator is created:
c = b.to_enum(:any?)
#=> #<Enumerator: [[ 5, 27, 45, 48, 70],
# [ 13, 19, 41, 51, 69],
# ["B", "B", "B", "B", "B"],
# [ 4, 18, 44, 58, 74],
# [ 10, 20, 31, 52, 73]]:any?>
When the enumerator (any enumerator) is acting on an array, the elements of the enumerator are passed into the block (and assigned to the block variable, here arr) by Array#each. The first element passed into the block is:
arr = [5, 27, 45, 48, 70]
and the following is executed:
arr.all? { |e| e == ?B }
#=> [5, 27, 45, 48, 70].all? { |e| e == ?B }
#=> false
Notice that false is returned to each right after:
5 == ?B
#=> false
is evaluated. Since false is returned, we move on to the second element of the enumerator:
[13, 19, 41, 51, 69].all? { |e| e == ?B }
#=> false
so we continue. But
["B", "B", "B", "B", "B"].all? { |e| e == ?B }
#=> true
so when true is returned to each, the latter returns true and we are finished.

Array select to get true and false arrays?

I know I can get this easily:
array = [45, 89, 23, 11, 102, 95]
lower_than_50 = array.select{ |n| n<50}
greater_than_50 = array.select{ |n| !n<50}
But is there a method (or an elegant manner) to get this by only running select once?
[lower_than_50, greater_than_50] = array.split_boolean{ |n| n<50}
over, under_or_equal = [45, 89, 23, 11, 102, 95].partition{|x| x>50 }
Or simply:
result = array.partition{|x| x>50 }
p result #=> [[89, 102, 95], [45, 23, 11]]
if you rather want the result as one array with two sub-arrays.
Edit: As a bonus, here is how you would to it if you have more than two alternatives and want to split the numbers:
my_custom_grouping = -> x do
case x
when 1..50 then :small
when 51..100 then :large
else :unclassified
end
end
p [-1,2,40,70,120].group_by(&my_custom_grouping) #=> {:unclassified=>[-1, 120], :small=>[2, 40], :large=>[70]}
The answer above is spot on!
Here is a general solution for more than two partitions (for example: <20, <50, >=50):
arr = [45, 89, 23, 11, 102, 95]
arr.group_by { |i| i < 20 ? 'a' : i < 50 ? 'b' : 'c' }.sort.map(&:last)
=> [[11], [45, 23], [89, 102, 95]]
This can be very useful if you're grouping by chunks (or any mathematically computable index such as modulo):
arr.group_by { |i| i / 50 }.sort.map(&:last)
=> [[45, 23, 11], [89, 95], [102]]

Creating a proc to check if an Integer is < 100

I'm blanking big time on this one. This is all i've got. Help all mighty Overflow gods!
ages = [23, 101, 7, 104, 11, 94, 100, 121, 101, 70, 44]
under_100 = Proc.new { |x| x < 100 }
Do as below
ages = [23, 101, 7, 104, 11, 94, 100, 121, 101, 70, 44]
under_100 = Proc.new { |x| x < 100 }
ages.select(&under_100) # => [23, 7, 11, 94, 70, 44]
ages.select { |x| x < 100 } # I would do this way or
ages.select(&100.method(:>)) # this way.
100.method(:>) created Method object(check this Object#method), same as the one you created as Proc.new { |x| x < 100 }. Now applying & to that proc/method object converting it to a block with #select method.
ages = [23, 101, 7, 104, 11, 94, 100, 121, 101, 70, 44]
under_100 = Proc.new { |x| x < 100 }
p under_100.call(201) #=> false
p under_100.call(23) #=> true
Map runs the proc on each element and returns the result:
p ages.map(&under_100) #=> [true, false, true, false, true, true, false, false, false, true, true]
Select goes through all elements and only returns the ones that evaluates to true:
p ages.select(&under_100) #=> [23, 7, 11, 94, 70, 44]
It could also be written like this:
result = []
ages.each{|age| result.push(age) if under_100.call(age)}
p result #=> [23, 7, 11, 94, 70, 44]
So that is why you do not need to use any if when using the select method of the Array.
without proc we can check it as
ages = [23, 101, 7, 104, 11, 94, 100, 121, 101, 70, 44]
ages.each do |x|
if x < 100;
puts "#{x} is less than 100";
end
end

Resources