3

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.

Nappstir
  • 995
  • 2
  • 20
  • 38
  • Your question indicates you are looking for an element of `board` (a row of the playing board) that contains all `x`'s, but your title and first sentence suggest you looking for an element of `board` whose elements are all the same, but not any particular value. Please edit to clarify whether all elements of a row must be the same or must equal a specified value. – Cary Swoveland Apr 27 '15 at 02:51
  • If you are not inclined to edit your question to clarify, I would appreciate the courtesy of a reply to my comment. – Cary Swoveland Apr 27 '15 at 15:57

5 Answers5

6

A bit more idiomatic:

board.one? { |row| row.all? { |item| item == 'x' } }

Marcus Ilgner
  • 6,935
  • 2
  • 30
  • 44
  • Interestingly enough I have tried both solutions. However, when attempting @kiddorails solution, the output still rings true if there is only one 'x', and I need it to ring true if only ALL are 'x'. I did use `board.one?` and that did work. Although I am unfamiliar with exactly how `.one?` works. – Nappstir Apr 26 '15 at 20:43
  • `Enumerable#one?` takes each entry in the container and passes it to the given block. If the block evaluates to `true` for exactly one record, the whole method returns `true`. – Marcus Ilgner Apr 26 '15 at 20:45
  • 1
    If you want strictly one row to have all "x", keep the `one?`. Otherwise use `any?`, which means at least one row is all "x" – Martin Konecny Apr 26 '15 at 20:50
2

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.

kiddorails
  • 12,961
  • 2
  • 32
  • 41
  • Added more information to play around with Ruby arrays. – kiddorails Apr 26 '15 at 20:42
  • This didn't seem to work when I ran it with board having only one 'x'. Still came back as true when I'm looking for it to only return true if all items within array are 'x'. – Nappstir Apr 26 '15 at 20:43
  • Can you give the `board` object in question in this case? Didn't fully get the scenario. – kiddorails Apr 26 '15 at 20:45
  • Beware that if the row is all filled with `1` or any other identical value, `uniq` will still return `true`, even though it's not `x`'s. – Marcus Ilgner Apr 26 '15 at 20:46
  • @ma_li: Travis stated " if all items within sub-arrays are the same", I don't think he is only expecting the `x` to be there to check the duplicates. `x` is just an example. – kiddorails Apr 26 '15 at 20:48
  • So take for example the commented out sub-array i just added. In that sub-array we have just one 'x'. This one 'x' returns true even though they aren't all true. – Nappstir Apr 26 '15 at 20:49
  • @TravisSiebenhaar It returns true only for 2nd row and not the commented row: `board.map { |row| row.uniq.count == 1 } #=> [false, true, false, false, false, false]`, `board.each.with_index.select { |row, index| row.uniq.count == 1 } #=> [[["x", "x", "x", "x", "x"], 1]]` – kiddorails Apr 26 '15 at 20:51
  • @kiddorails It appears your solution would be more ideal in a scenario where the user may be, like you said, expecting other items rather than just only the `x`. – Nappstir Apr 26 '15 at 20:51
  • Did I understand and answered your doubt correctly? The commented out row in your example will not return true by code above since, all elements in that subarray are NOT same. – kiddorails Apr 26 '15 at 20:54
  • This will not work fully, because we don't know for sure if we won't have an array of [12, 12, 12, 12, 12]. So you need to double check that the values are `'x'` @kiddorails – cdpalmer Jul 27 '15 at 17:45
0

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

marmeladze
  • 6,468
  • 3
  • 24
  • 45
  • if a row of board is `[1,'x']`, `[1,'x'].max #=> ArgumentError: comparison of String with 1 failed`. – Cary Swoveland Apr 27 '15 at 01:58
  • @CarySwoveland look at the sentence before the last one in OP. "but this will merely check if one of the integers is x and not all". – marmeladze Apr 27 '15 at 06:24
  • The penultimate sentence? :-) Yes, Travis is explaining why his code doesn't work. Suppose `board = [['x',1],['x','x']]`. Then your code returns `ArgumentError: comparison of Fixnum with String failed`. Specifically, it is `['x',1].max` that failed.. – Cary Swoveland Apr 27 '15 at 06:46
  • that means i've totally misunderstood :) i just thought that the `x` is just placeholder. there can be any other integer number and it seemed more rational to me – marmeladze Apr 27 '15 at 07:21
  • I'm pretty sure `'x'` is meant to be a string, because of the quotes. – Cary Swoveland Apr 27 '15 at 15:50
0

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
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
0

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

cdpalmer
  • 658
  • 1
  • 7
  • 19