Why does printing more than 2 values from this array cause an error? - arrays

Why does my code return an ArgumentError when printing more than 2 values from my array?
Code:
gamestate = Array['1','2','3','4','5','6','7','8','9']
player = 'X' #x is the first player#
turns = 0 # number of turns that have passed
puts "TICTACTOE"#prints initial screen{#
puts gamestate[0,1,2]
puts gamestate[3,4,5]
puts gamestate[6,7,8]
Error:
Traceback (most recent call last):
1: from C:/Users/Hunter/Desktop/programming/rubyprograms/Basics/tictactoe.rb:5:in `<main>'
C:/Users/Hunter/Desktop/programming/rubyprograms/Basics/tictactoe.rb:5:in `[]': wrong number of arguments (given 3, expected 1..2) (ArgumentError)
<hr>

Why does my code return an ArgumentError when printing more than 2 values from my array?
As the error message clearly says:
Traceback (most recent call last):
1: from C:/Users/Hunter/Desktop/programming/rubyprograms/Basics/tictactoe.rb:5:in `<main>'
C:/Users/Hunter/Desktop/programming/rubyprograms/Basics/tictactoe.rb:5:in `[]': wrong number of arguments (given 3, expected 1..2) (ArgumentError)
The error message gives error details, including a stack trace showing at what point in the execution the error occurred, and a filename and linenumber showing at what point in the source code the error occurred, but also an informational message:
wrong number of arguments (given 3, expected 1..2)
So, you passed three arguments to the method [] which however only accepts one or two arguments, as we can also clearly see in the documentation of Array#[]:
array[index] → object or nil
array[start, length] → object or nil
array[range] → object or nil
array[aseq] → object or nil
As you can see, there are four different ways that we can call the [] method:
with one Integer (or any object implicitly convertible to Integer) argument denoting the index of the object we want to retrieve,
with two Integer arguments, denoting the start index and the length of the subarray we want to retrieve,
with one Range argument, denoting the start and the end index of the subarray we want to retrieve, and
with one Enumerator::ArithmeticSequence argument, denoting the sequence of indices of the elements you want to retrieve.
While those are many different options, none take three arguments.
Since it looks like you always want exactly three consecutive elements starting from some specific index, you can use the second form here, which takes the starting index and the length of the subarray as arguments:
puts gamestate[0, 3]
puts gamestate[3, 3]
puts gamestate[6, 3]
If you wanted to retrieve multiple elements at arbitrary, non-consecutive indices instead, you could use Array#values_at instead.
By the way, there are a couple of non-idiomatic bits in your code:
Your style is inconsistent. You are sometimes using single quotes and sometimes using double quotes for no discernible reason. When there are two different ways to express something, and you are using those two different ways, people will assume that you want to express an important difference by using those two different styles. However, in this case, there doesn't seem to be any difference. It is generally preferred to always use single quotes unless you want to use string interpolation or need a particular escape sequence, or the string contains a single quote:
puts 'TICTACTOE'
There should be a space after a comma:
gamestate = Array['1', '2', '3', '4', '5', '6', '7', '8', '9']
and
puts gamestate[0, 1, 2]
puts gamestate[3, 4, 5]
puts gamestate[6, 7, 8]
There is no need to use the Array::[] method here, you can just the normal Array literal syntax instead:
gamestate = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
Even better, you could use a %w percent string literal:
gamestate = %w[1 2 3 4 5 6 7 8 9]
Alternatively, you could also use a Range and convert it to an Array:
gamestate = ('1'..'9').to_a
Since it looks like you are modeling a Tic Tat Toe game board with three rows of three cells each, a linear Array might not be the best possible way of representing it. A nested Array is probably easier to work with:
gamestate = [
%w[1 2 3],
%w[4 5 6],
%w[7 8 9]
]
The best representation, though, would be to represent your game state object as, you know, a GameState object, because that's what it is: a game state is a game state, not an array-of-strings, or an array-of-arrays-of-string. Ruby is an object-oriented language, after all, not an array-of-strings-oriented language.

Related

The number of same elements in an array

My aim is to display the number of identical elements in an array.
Here is my code:
a = [5, 2, 4, 1, 2]
b = []
for i in a
unless b.include?(a[i])
b << a[i]
print i," appears ",a.count(i)," times\n"
end
end
I get this output:
5 appears 1 times
2 appears 2 times
4 appears 1 times
The output misses 1.
Here's a different way to do it, assuming I understand what "it" is (counting elements in an array):
a = [5,2,4,1,2]
counts = a.each_with_object(Hash.new(0)) do |element, counter|
counter[element] += 1
end
# => {5=>1, 2=>2, 4=>1, 1=>1}
# i.e. one 5, two 2s, one 4, one 1.
counts.each do |element, count|
puts "#{element} appears #{count} times"
end
# => 5 appears 1 times
# => 2 appears 2 times
# => 4 appears 1 times
# => 1 appears 1 times
Hash.new(0) initialises a hash with a default value 0. We iterate on a (while passing the hash as an additional object), so element will be each element of a in order, and counter will be our hash. We will increment the value of the hash indexed by the element by one; on the first go for each element, there won't be anything there, but our default value saves our bacon (and 0 + 1 is 1). The next time we encounter an element, it will increment whatever value already is present in the hash under that index.
Having obtained a hash of elements and their counts, we can print them, of course, puts is same as print but automatically inserts a newline; and rather than using commas to print several things, it is much nicer to put the values directly into the printed string itself using the string interpolation syntax ("...#{...}...").
The problems in your code are as follows:
[logic] for i in a will give you elements of a, not indices. Thus, a[i] will give you nil for the first element, not 5, since a[5] is outside the list. This is why 1 is missing from your output: a[1] (i.e. 2) is already in b when you try to process it.
[style] for ... in ... is almost never seen in Ruby code, with strong preference to each and other methods of Enumerable module
[performance] a.count(i) inside a loop increases your algorithmic complexity: count itself has to see the whole array, and you need to iterate the array to see i, which will be exponentially slower with huge arrays. The method above only has one loop, as access to hashes is very fast, and thus grows more or less linearly with the size of the array.
The stylistic and performance problems are minor, of course; you won't see performance drop till you need to process really large arrays, and style errors won't make your code not work; however, if you're learning Ruby, you should aim to work with the language from the start, to get used to its idioms as you go along, as it will give you much stronger foundation than transplanting other languages' idioms onto it.
a = [5,2,4,1,2]
b = a.uniq
for i in b
print i," appears ",a.count(i)," times\n"
end
print b
Result:
5 appears 1 times
2 appears 2 times
4 appears 1 times
1 appears 1 times
[5, 2, 4, 1]

Is it safe to delete from an Array inside each?

Is it possible to safely delete elements from an Array while iterating over it via each? A first test looks promising:
a = (1..4).to_a
a.each { |i| a.delete(i) if i == 2 }
# => [1, 3, 4]
However, I could not find hard facts on:
Whether it is safe (by design)
Since which Ruby version it is safe
At some points in the past, it seems that it was not possible to do:
It's not working because Ruby exits the .each loop when attempting to delete something.
The documentation does not state anything about deletability during iteration.
I am not looking for reject or delete_if. I want to do things with the elements of an array, and sometimes also remove an element from the array (after I've done other things with said element).
Update 1: I was not very clear on my definition of "safe", what I meant was:
do not raise any exceptions
do not skip any element in the Array
You should not rely on unauthorized answers too much. The answer you cited is wrong, as is pointed out by Kevin's comment to it.
It is safe (from the beginning of Ruby) to delete elements from an Array while each in the sense that Ruby will not raise an error for doing that, and will give a decisive (i.e., not random) result.
However, you need to be careful because when you delete an element, the elements following it will be shifted, hence the element that was supposed to be iterated next would be moved to the position of the deleted element, which has been iterated over already, and will be skipped.
In order to answer your question, whether it is "safe" to do so, you will first have to define what you mean by "safe". Do you mean
it doesn't crash the runtime?
it doesn't raise an Exception?
it does raise an Exception?
it behaves deterministically?
it does what you expect it to do? (What do you expect it to do?)
Unfortunately, the Ruby Language Specification is not exactly helpful:
15.2.12.5.10 Array#each
each(&block)
Visibility: public
Behavior:
If block is given:
For each element of the receiver in the indexing order, call block with the element as the only argument.
Return the receiver.
This seems to imply that it is indeed completely safe in the sense of 1., 2., 4., and 5. above.
The documentation says:
each { |item| block } → ary
Calls the given block once for each element in self, passing that element as a parameter.
Again, this seems to imply the same thing as the spec.
Unfortunately, none of the currently existing Ruby implementations interpret the spec in this way.
What actually happens in MRI and YARV is the following: the mutation to the array, including any shifting of the elements and/or indices becomes visible immediately, including to the internal implementation of the iterator code which is based on array indices. So, if you delete an element at or before the position you are currently iterating, you will skip the next element, whereas if you delete an element after the position you are currently iterating, you will skip that element. For each_with_index, you will also observe that all elements after the deleted element have their indices shifted (or rather the other way around: the indices stay put, but the elements are shifted).
So, this behavior is "safe" in the sense of 1., 2., and 4.
The other Ruby implementations mostly copy this (undocumented) behavior, but being undocumented, you cannot rely on it, and in fact, I believe at least one did experiment briefly with raising some sort of ConcurrentModificationException instead.
I would say that it is safe, based on the following:
2.2.2 :035 > a = (1..4).to_a
=> [1, 2, 3, 4]
2.2.2 :036 > a.each { |i| a.delete(i+1) if i > 1 ; puts i }
1
2
4
=> [1, 2, 4]
I'd infer from this test that Ruby correctly recognises while iterating through the contents that the element "3" has been deleted while element "2" was being processed, otherwise element "4" would also have been deleted.
However,
2.2.2 :040 > a.each { |i| puts i; a.delete(i) if i > 1 ; puts i }
1
1
2
2
4
4
This suggests that after "2" is deleted, the next element processed is whichever is now third in the array, so the element that used to be in third place does not get processed at all. each appears to re-examine the array to find the next element to process on every iteration.
I think that with that in mind, you ought to duplicate the array in your circumstances prior to processing.
It depends.
All .each does is returns an enumerator, which holds the collection an a pointer to where it left. Example:
a = [1,2,3]
b = a.each # => #<Enumerator: [1, 2, 3]:each>
b.next # => 1
a.delete(2)
b.next # => 3
a.clear
b.next # => StopIteration: iteration reached an end
Each with block calls next until the iteration reaches its end. So as long as you don't modify any 'future' array records it should be safe.
However there are so many helpful methods in ruby's Enumerable and Array you really shouldn't ever need to do this.
You are right, in the past it was advised not to remove items from the collection while iterating over it. In my tests and at least with version 1.9.3 in practice in an array this gives no problem, even when deleting prior or next elements.
It is my opinion that while you can you shouldn't.
A more clear and safe approach is to reject the elements and assign to a new array.
b = a.reject{ |i| i == 2 } #[1, 3, 4]
In case you want to reuse your a array that is also possible
a = a.reject{ |i| i == 2 } #[1, 3, 4]
which is in fact the same as
a.reject!{ |i| i == 2 } #[1, 3, 4]
You say you don't want to use reject because you want to do other things with the elements before deleting, but that is also possible.
a.reject!{ |i| puts i if i == 2;i == 2 }
# 2
#[1, 3, 4]

Breaking up a string and then converting to a vector?

If I have three strings, such as 'Y20194', '219Y42', and '12345' how do I break these up into a vector like [Y 2 0 1 9 4], [1 2 3 4 5], and [2 1 9 Y 4 2]? I am using str2num, but I think I am missing a step (separating the individual numbers in the strings first) before I convert to numerical values. Also, the characters aren't reading correctly and using str2num gives me [].
I have a file with lines of strings such as the one above. I used fgetl to read each line of my file into strings but am kind of stuck beyond that.
You cannot have both characters and numbers in a numerical vector.
You can do the following:
s = 'Y20194';
c = cellstr(s')';
v = str2double(c);
Cell array c will have all the characters from s separated in to individual cells. Notice that you have to transpose the string s first.
In vector v the first value will be NaN since it's a character.
The char will be kept. and the numbers will be converted to double type.
If the input is not from reading a file, the code is as follows. The result1 is the cell containing the array you want:
If the input is one file, let's take this file as example: demo1.txt, which content as follows:
the codes to convert each line to what you want as follows. the code converts each line into what you want and then display it.
If you want to replace the 'Y' or other alphabets with zero, then the code will be as follows
Maybe STRSPLIT will help.
ts = strsplit('Y20194');
% ts <- {'Y', '2', '0', '1', '9', '4'}
And now you can try to convert each element in the vector individually to a number using str2num.
N = size(ts, 1);
str = cell(1, N);
for i=1:N;
str{i} = str2num(ts{i, 1});
end
But since some of the characters in the string aren't numbers (e.g., 'Y'), I wouldn't expect this to work perfectly.
(Its been a while, some of my indexes may be switched.)
If you want to change the Y into 0 a very simple solution is available:
str = 'Y20194';
str(str==Y)='0';
str - '0'

Rails, evaluate if numbers are in sequence

Is there a way to evaluate the order of an array? I want to find an array of records ordered by created_at date and then see if the numbers in that array are in sequence?
For instance:
Model.all.order(&:created_at).select("lesson_number)
[1, 2, 4, 3, 5]
should fail because the numbers are not in sequence
I could execute two finds. One that is ordered by "lesson_number" and one that is ordered by created_at date. Convert them both the a string then compare the two. But, seems like a lot of work if a rails method exists to handle such a thing.
You can combine a couple methods in Ruby to do this pretty efficiently.
input.each_cons(2).reduce(true) { |result, (a, b)| result && (a <=> b) < 0 }
each_cons will iterate through your array yielding, in this case, each 2 consecutive items. Because we don't pass it a block, it returns an enumerator that we can iterate through and get a single resulting value using reduce (a.k.a. inject).
Our block compares a and b using <=> which will return -1, 0, or 1 depending on whether the first value is "less-than", equal, or "greater than". In this case, we want to make sure a is -1.
In case you're not familiar with it, the parenthesis in the block argument are Ruby 1.9+, and they allow the arguments to be splatted in (otherwise we would get a 2-item array in our block).

Algorithm to find "most common elements" in different arrays

I have for example 5 arrays with some inserted elements (numbers):
1,4,8,10
1,2,3,4,11,15
2,4,20,21
2,30
I need to find most common elements in those arrays and every element should go all the way till the end (see example below). In this example that would be the bold combination (or the same one but with "30" on the end, it's the "same") because it contains the smallest number of different elements (only two, 4 and 2/30).
This combination (see below) isn't good because if I have for ex. "4" it must "go" till it ends (next array mustn't contain "4" at all). So combination must go all the way till the end.
1,4,8,10
1,2,3,4,11,15
2,4,20,21
2,30
EDIT2: OR
1,4,8,10
1,2,3,4,11,15
2,4,20,21
2,30
OR anything else is NOT good.
Is there some algorithm to speed this thing up (if I have thousands of arrays with hundreds of elements in each one)?
To make it clear - solution must contain lowest number of different elements and the groups (of the same numbers) must be grouped from first - larger ones to the last - smallest ones. So in upper example 4,4,4,2 is better then 4,2,2,2 because in first example group of 4's is larger than group of 2's.
EDIT: To be more specific. Solution must contain the smallest number of different elements and those elements must be grouped from first to last. So if I have three arrrays like
1,2,3
1,4,5
4,5,6
Solution is 1,1,4 or 1,1,5 or 1,1,6 NOT 2,5,5 because 1's have larger group (two of them) than 2's (only one).
Thanks.
EDIT3: I can't be more specific :(
EDIT4: #spintheblack 1,1,1,2,4 is the correct solution because number used first time (let's say at position 1) can't be used later (except it's in the SAME group of 1's). I would say that grouping has the "priority"? Also, I didn't mention it (sorry about that) but the numbers in arrays are NOT sorted in any way, I typed it that way in this post because it was easier for me to follow.
Here is the approach you want to take, if arrays is an array that contains each individual array.
Starting at i = 0
current = arrays[i]
Loop i from i+1 to len(arrays)-1
new = current & arrays[i] (set intersection, finds common elements)
If there are any elements in new, do step 6, otherwise skip to 7
current = new, return to step 3 (continue loop)
print or yield an element from current, current = arrays[i], return to step 3 (continue loop)
Here is a Python implementation:
def mce(arrays):
count = 1
current = set(arrays[0])
for i in range(1, len(arrays)):
new = current & set(arrays[i])
if new:
count += 1
current = new
else:
print " ".join([str(current.pop())] * count),
count = 1
current = set(arrays[i])
print " ".join([str(current.pop())] * count)
>>> mce([[1, 4, 8, 10], [1, 2, 3, 4, 11, 15], [2, 4, 20, 21], [2, 30]])
4 4 4 2
If all are number lists, and are all sorted, then,
Convert to array of bitmaps.
Keep 'AND'ing the bitmaps till you hit zero. The position of the 1 in the previous value indicates the first element.
Restart step 2 from the next element
This has now turned into a graphing problem with a twist.
The problem is a directed acyclic graph of connections between stops, and the goal is to minimize the number of lines switches when riding on a train/tram.
ie. this list of sets:
1,4,8,10 <-- stop A
1,2,3,4,11,15 <-- stop B
2,4,20,21 <-- stop C
2,30 <-- stop D, destination
He needs to pick lines that are available at his exit stop, and his arrival stop, so for instance, he can't pick 10 from stop A, because 10 does not go to stop B.
So, this is the set of available lines and the stops they stop on:
A B C D
line 1 -----X-----X-----------------
line 2 -----------X-----X-----X-----
line 3 -----------X-----------------
line 4 -----X-----X-----X-----------
line 8 -----X-----------------------
line 10 -----X-----------------------
line 11 -----------X-----------------
line 15 -----------X-----------------
line 20 -----------------X-----------
line 21 -----------------X-----------
line 30 -----------------------X-----
If we consider that a line under consideration must go between at least 2 consecutive stops, let me highlight the possible choices of lines with equal signs:
A B C D
line 1 -----X=====X-----------------
line 2 -----------X=====X=====X-----
line 3 -----------X-----------------
line 4 -----X=====X=====X-----------
line 8 -----X-----------------------
line 10 -----X-----------------------
line 11 -----------X-----------------
line 15 -----------X-----------------
line 20 -----------------X-----------
line 21 -----------------X-----------
line 30 -----------------------X-----
He then needs to pick a way that transports him from A to D, with the minimal number of line switches.
Since he explained that he wants the longest rides first, the following sequence seems the best solution:
take line 4 from stop A to stop C, then switch to line 2 from C to D
Code example:
stops = [
[1, 4, 8, 10],
[1,2,3,4,11,15],
[2,4,20,21],
[2,30],
]
def calculate_possible_exit_lines(stops):
"""
only return lines that are available at both exit
and arrival stops, discard the rest.
"""
result = []
for index in range(0, len(stops) - 1):
lines = []
for value in stops[index]:
if value in stops[index + 1]:
lines.append(value)
result.append(lines)
return result
def all_combinations(lines):
"""
produce all combinations which travel from one end
of the journey to the other, across available lines.
"""
if not lines:
yield []
else:
for line in lines[0]:
for rest_combination in all_combinations(lines[1:]):
yield [line] + rest_combination
def reduce(combination):
"""
reduce a combination by returning the number of
times each value appear consecutively, ie.
[1,1,4,4,3] would return [2,2,1] since
the 1's appear twice, the 4's appear twice, and
the 3 only appear once.
"""
result = []
while combination:
count = 1
value = combination[0]
combination = combination[1:]
while combination and combination[0] == value:
combination = combination[1:]
count += 1
result.append(count)
return tuple(result)
def calculate_best_choice(lines):
"""
find the best choice by reducing each available
combination down to the number of stops you can
sit on a single line before having to switch,
and then picking the one that has the most stops
first, and then so on.
"""
available = []
for combination in all_combinations(lines):
count_stops = reduce(combination)
available.append((count_stops, combination))
available = [k for k in reversed(sorted(available))]
return available[0][1]
possible_lines = calculate_possible_exit_lines(stops)
print("possible lines: %s" % (str(possible_lines), ))
best_choice = calculate_best_choice(possible_lines)
print("best choice: %s" % (str(best_choice), ))
This code prints:
possible lines: [[1, 4], [2, 4], [2]]
best choice: [4, 4, 2]
Since, as I said, I list lines between stops, and the above solution can either count as lines you have to exit from each stop or lines you have to arrive on into the next stop.
So the route is:
Hop onto line 4 at stop A and ride on that to stop B, then to stop C
Hop onto line 2 at stop C and ride on that to stop D
There are probably edge-cases here that the above code doesn't work for.
However, I'm not bothering more with this question. The OP has demonstrated a complete incapability in communicating his question in a clear and concise manner, and I fear that any corrections to the above text and/or code to accommodate the latest comments will only provoke more comments, which leads to yet another version of the question, and so on ad infinitum. The OP has gone to extraordinary lengths to avoid answering direct questions or to explain the problem.
I am assuming that "distinct elements" do not have to actually be distinct, they can repeat in the final solution. That is if presented with [1], [2], [1] that the obvious answer [1, 2, 1] is allowed. But we'd count this as having 3 distinct elements.
If so, then here is a Python solution:
def find_best_run (first_array, *argv):
# initialize data structures.
this_array_best_run = {}
for x in first_array:
this_array_best_run[x] = (1, (1,), (x,))
for this_array in argv:
# find the best runs ending at each value in this_array
last_array_best_run = this_array_best_run
this_array_best_run = {}
for x in this_array:
for (y, pattern) in last_array_best_run.iteritems():
(distinct_count, lengths, elements) = pattern
if x == y:
lengths = tuple(lengths[:-1] + (lengths[-1] + 1,))
else :
distinct_count += 1
lengths = tuple(lengths + (1,))
elements = tuple(elements + (x,))
if x not in this_array_best_run:
this_array_best_run[x] = (distinct_count, lengths, elements)
else:
(prev_count, prev_lengths, prev_elements) = this_array_best_run[x]
if distinct_count < prev_count or prev_lengths < lengths:
this_array_best_run[x] = (distinct_count, lengths, elements)
# find the best overall run
best_count = len(argv) + 10 # Needs to be bigger than any possible answer.
for (distinct_count, lengths, elements) in this_array_best_run.itervalues():
if distinct_count < best_count:
best_count = distinct_count
best_lengths = lengths
best_elements = elements
elif distinct_count == best_count and best_lengths < lengths:
best_count = distinct_count
best_lengths = lengths
best_elements = elements
# convert it into a more normal representation.
answer = []
for (length, element) in zip(best_lengths, elements):
answer.extend([element] * length)
return answer
# example
print find_best_run(
[1,4,8,10],
[1,2,3,4,11,15],
[2,4,20,21],
[2,30]) # prints [4, 4, 4, 30]
Here is an explanation. The ...this_run dictionaries have keys which are elements in the current array, and they have values which are tuples (distinct_count, lengths, elements). We are trying to minimize distinct_count, then maximize lengths (lengths is a tuple, so this will prefer the element with the largest value in the first spot) and are tracking elements for the end. At each step I construct all possible runs which are a combination of a run up to the previous array with this element next in sequence, and find which ones are best to the current. When I get to the end I pick the best possible overall run, then turn it into a conventional representation and return it.
If you have N arrays of length M, this should take O(N*M*M) time to run.
I'm going to take a crack here based on the comments, please feel free to comment further to clarify.
We have N arrays and we are trying to find the 'most common' value over all arrays when one value is picked from each array. There are several constraints 1) We want the smallest number of distinct values 2) The most common is the maximal grouping of similar letters (changing from above for clarity). Thus, 4 t's and 1 p beats 3 x's 2 y's
I don't think either problem can be solved greedily - here's a counterexample [[1,4],[1,2],[1,2],[2],[3,4]] - a greedy algorithm would pick [1,1,1,2,4] (3 distinct numbers) [4,2,2,2,4] (two distinct numbers)
This looks like a bipartite matching problem, but I'm still coming up with the formulation..
EDIT : ignore; This is a different problem, but if anyone can figure it out, I'd be really interested
EDIT 2 : For anyone that's interested, the problem that I misinterpreted can be formulated as an instance of the Hitting Set problem, see http://en.wikipedia.org/wiki/Vertex_cover#Hitting_set_and_set_cover. Basically the left hand side of the bipartite graph would be the arrays and the right hand side would be the numbers, edges would be drawn between arrays that contain each number. Unfortunately, this is NP complete, but the greedy solutions described above are essentially the best approximation.

Resources