So I've been trying to solve this problem and am kinda stuck at implementing it using backtracking and would really like to understand what I'm doing wrong here. I suspect my problem is something to do with arrays being passed by reference in the methods, but can't seem to put my finger on it. Any help is appreciated. This was asked in an interview and I am trying to solve it on my own.
Here is the question:
Cards have a suite and repeating time. Given a list of cards and output size, return a list with cards that have
(all same letter or all different letters) &
(all the same length or all different letter lengths).
exaple1:
input: ['X', 'Y', 'YY', 'ZZ', 'ZZZ'], 3
output: ['X', 'YY', 'ZZZ']
-------------
example2:
input: ['Y', 'Y', 'Z', 'ZZ', 'X', 'XX'], 3
output: ['Y', 'Z', 'X']
-------------
example3:
input: ['X', 'Y', 'YY', 'ZZ', 'ZZZ'], 3
output: ['X', 'YY', 'ZZZ']
My algorithm is as follows:
call a helper method that returns if such a combo exists and the combo
the helper method takes the list of cards and the number of cards in the output that's needed
base case: return true and empty array if count is 0 (meaning we have no more cards to pick)
iterate over each card
find a combo for count - 1 cards with the current card being picked (recursively)
if a combo is found, do checks with current card to make sure it can be added to the current combination and return
if you've iterated over all cards and cannot find a combo, return false with empty array
def unique_cards(cards, count)
can, picked_cards = _unique_cards(cards, count)
puts can ? picked_cards : false
end
def _unique_cards(cards, count)
return [true, []] if count == 0
cards.each do |card|
card_length = card.length
card_type = card[0]
remaining_cards = cards - [card]
can, selected_cards = _unique_cards(remaining_cards, count - 1)
if can
can_be_added = (selected_cards.all? { |c1| c1.length == card_length } || selected_cards.all? { |c1| c1[0] == card_type }) && (selected_cards.all? { |c1| c1.length != card_length } || selected_cards.all? { |c1| c1[0] != card_type })
if can_be_added
selected_cards << card
return [true, selected_cards]
end
end
end
return [false, []]
end
require 'set'
def find_em(arr, n)
arr.combination(n)
.select do |a|
[1, n].include?(a.map { |s| s[0] }.uniq.size) &&
[1, n].include?(a.map(&:size).uniq.size)
end.uniq(&:to_set)
end
find_em ['Y', 'Y', 'Z', 'ZZ', 'X', 'XX', 'Y'], 3
#=> [["Y", "Y", "Y"], ["Y", "Z", "X"]]
find_em ['X', 'Y', 'YY', 'ZZ', 'ZZZ'], 3
#=> [["X", "YY", "ZZZ"]]
find_em ['X', 'Y', 'YY', 'ZZ', 'ZZ'], 3
#=> []
In the first example, without .uniq(&:to_set) we would obtain
find_em ['Y', 'Y', 'Z', 'ZZ', 'X', 'XX', 'Y'], 3
#=> [["Y", "Y", "Y"], ["Y", "Z", "X"], ["Y", "Z", "X"], ["Z", "X", "Y"]]
See Array#combination and Array#uniq.
Related
I have an array
a = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
and another array:
b = [0, 3, 6, 3, 4, 0, 1]
Is it possible to sort array a according to values in array b?
The result should be:
a = ['c', 'e', 'b', 'd', 'g', 'a', 'f']
Something like this doesn't seem to exist in Ruby/Rails:
a.sort_with_index{ |elem, index| b[index] }
Edit: in response to the duplicate marking: the question being referred to has an array with elements having an ID, and the other array references the ID's directly. This is not the same, and the same solution will not apply...
a.sort_by.with_index { |_,i| [-b[i], i] }
#=> ["c", "e", "b", "d", "g", "a", "f"]
This uses the indices of elements in a to break ties. I see from a comment on #tadman's answer that that is desired, though it is not a requirement given in the statement of the question. See the third paragraph of the doc for Array#<=> for an explanation of how arrays are ordered in sorting operations.
You can just combine the two, sort, and strip out the original a values:
a.zip(b).sort_by { |_a, _b| -_b }.map { |_a,_| _a }
Trying to check if all items within sub-arrays are the same. For example, I have a 5x5 board and I want to know if one of the arrays contains all x's:
board = [[47, 44, 71, 8, 88],
['x', 'x', 'x', 'x', 'x'],
# [83, 85, 97, 'x', 57],
[83, 85, 97, 89, 57],
[25, 31, 96, 68, 51],
[75, 70, 54, 80, 83]]
I currently have:
def check_x
board.each do |x|
return true if x.include?('x')
end
return false
end
But this will merely check if one of the integers is x and not all. Any suggestions would be greatly appreciated.
A bit more idiomatic:
board.one? { |row| row.all? { |item| item == 'x' } }
As simple as board.map { |row| row.uniq.count == 1 } will do
#=> [false, true, false, false, false]
uniq returns unique elements in an array. map here is iterating over your array and passing one row at a time to the block. It will return true for cases where all elements in an array are same (['x', 'x', 'x', 'x', 'x'].uniq #=> ['x'] whose length is 1)
If you just want to check if any row in board has all duplicate elements, ruby has just a function. Guess what? any?. Just change above one-liner with any? as:
board.any? { |row| row.uniq.count == 1 } #=> true
If you want to find out which row(s) has/have all the duplicates, and what duplicate it has:
board.each.with_index.select { |row, index| row.uniq.count == 1 }
#=> [[["x", "x", "x", "x", "x"], 1]], where 1 is index.
Pure Ruby awesomeness.
if all elements are same in an array, that means maximum and minimum is equal.
for your board you can find index of desired sub-array with this one line
board.each {|b| puts board.index(b) if b.max == b.min}
or just replace x.include?("x") with x.min == x.max in your function for true/false result
Assuming all elements of board (rows of the board) are the same size, which seems a reasonable assumption, you could do it thus:
x_row = ['x']*board.first.size
#=> ["x", "x", "x", "x", "x"]
board.any? { |row| row == x_row }
#=> true
Assuming it's always a fixed length array, your method can just be:
def full_row
board.each do |row|
return true if (row.uniq.count == 1) && (row[0] == 'x')
end
return false
end
This could be boiled down to fewer lines, but I hate line wrapping in vim :p
I am new to Ruby and stuck with this issue. Let's say I have an array like this:
arr = [1, 2, 's', nil, '', 'd']
and I want to remove nil and blank string from it, i.e. final array should be:
arr = [1, 2, 's', 'd']
I tried compact but it gives this:
arr.compact!
arr #=> [1, 2, 's', '', 'd'] doesn't remove empty string.
I was wondering if there's a smart way of doing this in Ruby.
You could do this:
arr.reject { |e| e.to_s.empty? } #=> [1, 2, "s", "d"]
Note nil.to_s => ''.
Since you want to remove both nil and empty strings, it's not a duplicate of How do I remove blank elements from an array?
You want to use .reject:
arr = [1, 2, 's', nil, '', 'd']
arr.reject { |item| item.nil? || item == '' }
NOTE: reject with and without bang behaves the same way as compact with and without bang: reject! and compact! modify the array itself while reject and compact return a copy of the array and leave the original intact.
If you're using Rails, you can also use blank?. It was specifically designed to work on nil, so the method call becomes:
arr.reject { |item| item.blank? }
I tend to do:
arr = [1, 2, 's', nil, '', 'd']
arr.reject(&:blank?)
returns:
=> [1, 2, "s", "d"]
arr.reject(&:blank?)
Just use this, no need to anything else.
compact_blank (Rails 6.1+)
If you are using Rails (or a standalone ActiveSupport), starting from version 6.1, there is a compact_blank method which removes blank values from arrays.
It uses Object#blank? under the hood for determining if an item is blank.
[1, 2, 's', nil, '', 'd'].compact_blank
# => [1, 2, 's', 'd']
[1, "", nil, 2, " ", [], {}, false, true].compact_blank
# => [1, 2, true]
Here is a link to the docs and a link to the relative PR.
A destructive variant is also available. See Array#compact_blank!.
You can also use - to remove all nil and '' elements:
arr -= [nil, '']
#=> [1, 2, "s", "d"]
Demonstration
Or compact and reject with shortcut (in case you are not using Rails where you can just use arr.reject(&:blank?) ):
arr = arr.compact.reject(&''.method(:==))
#=> [1, 2, "s", "d"]
Demonstration
You can use compact with reject
arr = [1, 2, 's', nil, '', 'd']
arr = [1, 2, 's', 'd']
arr = arr.compact.reject { |h| h == "" }
or
arr = arr.compact.delete_if { |h| h == "" }
The simplest and fast way of doing this is :
arr = [1, 2, 's', nil, '', 'd'] - [nil,'']
==> arr = [1, 2, 's', 'd']
You can use compact and delete_if method to remove nil and blank string in an array in Ruby
arr = [1, 2, 's', nil, '', 'd']
arr.compact!.delete_if{|arrVal| arrVal.class == String and arrVal.empty?}
=> [1, 2, "s", "d"]
try this out:
[1, 2, "s", nil, "", "d"].compact.select{|i| !i.to_s.empty?}
Note: I am considering the array might have string with white spaces in it.
You can do:
arr = [1, 2, 's', nil, ' ', 'd']
arr.reject{|a| a.nil? || (a.to_s.gsub(' ', '') == '') }
#=> [1, 2, "s", "d"]
or:
arr.reject{|a| a.nil? || (a.to_s.gsub(' ', '').empty?) }
#=> [1, 2, "s", "d"]
or if you want to update arr object itself then:
arr.reject!{|a| a.nil? || (a.to_s.gsub(' ', '') == '') } # notice the ! mark, it'll update the object itself.
p arr #=> [1, 2, "s", "d"]
Hope this will work for your case :
arr = [1, 2, 's', nil, '', 'd']
arr.select{|x| x.to_s!="" }
I would probably add .strip to eliminate potential whitespace headaches (assuming its not a rails app).
array = [1, 2, "s", nil, " ", "d", "\n"]
array.reject!{|a| a.nil? || (a.to_s.strip.empty?) }
#=> [1, 2, "s", "d"]
I have an array (outside array) that contains three arrays (inside arrays), each of which have three elements.
array = [[a, b, c], [d, e, f], [g, h, i]]
I want to select the specific inside array using an index of the outside array and then select the value within the selected inside array based off its index. Here is what I tried:
array.each_index{|i| puts "letter: #{array[i[3]]} " }
I was hoping that would give me the following output
letter: c letter: f letter: i
but instead, I get
letter: [[a, b, c], [d, e, f], [g, h, i]]
I also tried
array.each_index{|i| puts "letter: #{array[i][3]} " }
but I get the same result. Please any suggestions are very appreciated. I need a simple explanation.
each_index is an Enumerable which goes through all indices and performs an action on each one. When it's done it will return your original collection as it's not its job to change it. If you want to output stuff on the screen via puts / print then each_index is fine.
If you want to create a new collection as a result of going through all the elements of an original collection, you should use map.
e.g.
array = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
new_array = array.map {|el| el[2]}
=> ["c", "f", "i"]
array.map iterates through array's elements so in every step |el| is an element, not an index, as in: ['a', 'b', 'c'] in the first iteration, ['d', 'e', 'f'] in the second one and so on...
Just pointing this out since I don't know what's the goal of what you're trying to do.
do it like this:
array.each_index{|i| puts "letter: #{array[i][2]} " }
Since you want letter at index 2, not 3.
Also array should be defined like this:
array = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
You could use map like so:
a = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
a.map(&:last)
# => ["c", "f", "i"]
Or if you really want the puts and not the collected values:
a.each {|v| puts "letter: #{v.last}"}
You could also use Ruby's Matrix class if there's more Matrix-y stuff you want to do:
require 'matrix'
m = Matrix[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
last_column = m.column_vectors.last
# => Vector["c", "f", "i"]
You can now use any of the Vector methods on last_column, including to_a, which will put you back in familiar territory with the last column's values.
Arrays in ruby are indexed from 0, not from 1. So:
array.each_index{|i| puts "letter: #{array[i][2]} " }
should give you what you want.
You could try the below also:
p RUBY_VERSION
arr = [[1,2,3],[4,5,6],[11,12,13]]
arr.each{|x| p x; x.each_index{|i| p "Digit :: #{x[i]}" if i == 2} }
output:
"2.0.0"
[1, 2, 3]
"Digit :: 3"
[4, 5, 6]
"Digit :: 6"
[11, 12, 13]
"Digit :: 13
What's a better way to traverse an array while iterating through another array? For example, if I have two arrays like the following:
names = [ "Rover", "Fido", "Lassie", "Calypso"]
breeds = [ "Terrier", "Lhasa Apso", "Collie", "Bulldog"]
Assuming the arrays correspond with one another - that is, Rover is a Terrier, Fido is a Lhasa Apso, etc. - I'd like to create a dog class, and a new dog object for each item:
class Dog
attr_reader :name, :breed
def initialize(name, breed)
#name = name
#breed = breed
end
end
I can iterate through names and breeds with the following:
index = 0
names.each do |name|
Dog.new("#{name}", "#{breeds[index]}")
index = index.next
end
However, I get the feeling that using the index variable is the wrong way to go about it. What would be a better way?
dogs = names.zip(breeds).map { |name, breed| Dog.new(name, breed) }
Array#zip interleaves the target array with elements of the arguments, so
irb> [1, 2, 3].zip(['a', 'b', 'c'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
You can use arrays of different lengths (in which case the target array determines the length of the resulting array, with the extra entries filled in with nil).
irb> [1, 2, 3, 4, 5].zip(['a', 'b', 'c'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'], [4, nil], [5, nil] ]
irb> [1, 2, 3].zip(['a', 'b', 'c', 'd', 'e'])
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
You can also zip more than two arrays together:
irb> [1,2,3].zip(['a', 'b', 'c'], [:alpha, :beta, :gamma])
#=> [ [1, 'a', :alpha], [2, 'b', :beta], [3, 'c', :gamma] ]
Array#map is a great way to transform an array, since it returns an array where each entry is the result of running the block on the corresponding entry in the target array.
irb> [1,2,3].map { |n| 10 - n }
#=> [ 9, 8, 7 ]
When using iterators over arrays of arrays, if you give a multiple parameter block, the array entries will be automatically broken into those parameters:
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each { |array| p array }
[ 1, 'a' ]
[ 2, 'b' ]
[ 3, 'c' ]
#=> nil
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each do |num, char|
...> puts "number: #{num}, character: #{char}"
...> end
number 1, character: a
number 2, character: b
number 3, character: c
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
Like Matt Briggs mentioned, #each_with_index is another good tool to know about. It iterates through the elements of an array, passing a block each element in turn.
irb> ['a', 'b', 'c'].each_with_index do |char, index|
...> puts "character #{char} at index #{index}"
...> end
character a at index 0
character b at index 1
character c at index 2
#=> [ 'a', 'b', 'c' ]
When using an iterator like #each_with_index you can use parentheses to break up array elements into their constituent parts:
irb> [ [1, 'a'], [2, 'b'], [3, 'c'] ].each_with_index do |(num, char), index|
...> puts "number: #{num}, character: #{char} at index #{index}"
...> end
number 1, character: a at index 0
number 2, character: b at index 1
number 3, character: c at index 2
#=> [ [1, 'a'], [2, 'b'], [3, 'c'] ]
each_with_index leaps to mind, it is a better way to do it the way you are doing it. rampion has a better overall answer though, this situation is what zip is for.
This is adapted from Flanagan and Matz, "The Ruby Programming Language", 5.3.5 "External Iterators", Example 5-1, p. 139:
++++++++++++++++++++++++++++++++++++++++++
require 'enumerator' # needed for Ruby 1.8
names = ["Rover", "Fido", "Lassie", "Calypso"]
breeds = ["Terrier", "Lhasa Apso", "Collie", "Bulldog"]
class Dog
attr_reader :name, :breed
def initialize(name, breed)
#name = name
#breed = breed
end
end
def bundle(*enumerables)
enumerators = enumerables.map {|e| e.to_enum}
loop {yield enumerators.map {|e| e.next} }
end
bundle(names, breeds) {|x| p Dog.new(*x) }
+++++++++++++++++++++++++++++++++++++++++++
Output:
#<Dog:0x10014b648 #name="Rover", #breed="Terrier">
#<Dog:0x10014b0d0 #name="Fido", #breed="Lhasa Apso">
#<Dog:0x10014ab80 #name="Lassie", #breed="Collie">
#<Dog:0x10014a770 #name="Calypso", #breed="Bulldog">
which I think is what we wanted!
As well as each_with_index (mentioned by Matt), there's each_index. I sometimes use this because it makes the program more symmetrical, and therefore wrong code will look wrong.
names.each_index do |i|
name, breed = dogs[i], breeds[i] #Can also use dogs.fetch(i) if you want to fail fast
Dog.new(name, breed)
end