In the following, the results are the same:
[3, 5].sort{|a, b| b <=> a}
[5, 3].sort{|a, b| b <=> a}
I would like to know what happened internally and how it depends on input array.
The first line:
[3, 5].sort { |a, b| b <=> a }
Invokes the block with a = 3 and b = 5. It returns the result of 5 <=> 3 which is 1. An integer greater than 0 tells sort that a follows b. The result is therefore [5, 3].
The second line:
[5, 3].sort { |a, b| b <=> a }
Invokes the block with a = 5 and b = 3. It returns the result of 3 <=> 5 which is -1. An integer less than 0 tells sort that b follows a. The result is therefore (again) [5, 3].
Because you are sorting an array, and changing the array's elements order does not change the sorting result.
This is the whole point of sorting after all - to get sorted result despite the initial arrays ordering.
To change the result, you will want to change the sorting rule, not the array.
The output is the same regardless of the input order because you sorting the array.
If you want to sort with the opposite order write
[3,5].sort{|a,b| a <=> b}
Related
I am learning python numpy.array and am confused about how the index works. Let's see I have the following 3x4 2D array:
A = np.array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9,10,11,12]])
If I want to extract the 1 from this array, I need to input the index of that number, which is A[0,0]
Out of curiosity I also tried the following
B = A[[0,0]]
C = A[[0],[0]]
B turns out to be a 2x4 2D array:
array([[1, 2, 3, 4],
[1, 2, 3, 4]])
C turns out to be a 1D array of 1 element:
array([1])
I am wondering how indexing of B and C works and why I obtain those arrays?
In B, you are only giving one index for a 2 dimensional array which is [0,0]. So it will return the element in the first dimension of the index given (0 and 0 here).
So, for the first index (which is 0) it will return the first element in the first dimension which is [1,2,3,4] and it will go for the next index given which is again 0, so it will print two [1,2,3,4] as you have got.
Next in the C, you have given 2 indices for a 2 dimensional array which are [0] and [0]. So it will go through the first dimension for the index 0 which is [1,2,3,4] and in that element it will return the 0th position which is [1] as you have got.
For better understanding, let's see another case A[[0,1],2].
Here, we have given 2 indices for a 2 dimensional array which are [0,1] and 2. So, we get the elements which are in the index [0,2] and next with [1,2].Th output will be [3,7].
The thing is it will iterate through all possible combinations of indices given and return those values in those indices.
I have a nested array in ruby, similar to the one below, but with quite a few more nested arrays.
arr=[[1,2,3],[4,5,6],[7,8,9]]
Is there a way to get every second element within the nested array, and add x to that element? I have used the code below, but if there is a quicker way I'd love to know.
x = 5
arr[0][1], arr[1][1], arr[2][1] = arr[0][1]+x, arr[1][1]+x, arr[2][1]+x
I have tried to use arr.map!{|f,l| [f, l + 1]}, but I get the result arr == [[1, 3], [4, 6], [7, 9]]
EDIT:
So the outcome should be arr == [[1,2+x,3],[4,5+x,6],[7,8+x,9]]
Any time you find yourself writing that kind of code over and over, you should look to use a loop! Ruby commonly uses iterators for performing looping. Array#each is an iterator which loops over an Array, and lets you operate on each element one at a time.
Since you know that you want to add x to the second element in each, this is trivially:
arr.each {|e| e[1] += x }
This mutates arr (and arr's subarrays) in place. If you want to return a mutated copy, you would use map, which is like each except the return value is the output of the block, rather than the input to it.
# First we clone each subarray with Array#dup, then we iterate the
# clones, adding x to the second element. Because we're using `map`
# the result will be a new array, rather than the original arr, leaving
# the original unmodified.
new_arr = arr.map(&:dup).each {|e| e[1] += x }
You're close! You can use map to loop through each sub-array and the ruby += operator to add x to the second element of each. The trick with map is that you'll need to return the entire sub-array in each loop, which would look like:
arr.map { |a| a[1] += x; a }
#=> [[1, 7, 3], [4, 10, 6], [7, 13, 9]]
Here's another one:
arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x = 5
arr.map { |a, b, c| [a, b + x, c] }
#=> [[1, 7, 3], [4, 10, 6], [7, 13, 9]]
Note that map returns a new array. If you want to modify the existing array, there's map!.
This question already has an answer here:
How do I do stable sort?
(1 answer)
Closed 3 years ago.
I am implementing a custom sort. There is the spaceship operator <=> to deal with sorting an array:
myArray.sort { |a, b| a <=> b }
a <=> b returns 1 when b is larger than a, and the two elements are swapped.
a <=> b returns 0 when a equals to b, and the two elements stay in the original position.
a <=> b returns -1 when a is less than b, and the two element stay in the original position.
So I tested with an example:
myArray = [2, 1, 7, 9, 3, 8, 0]
myArray.sort { |a, b| 1 <=> 1 } # making it always return 0
#=> [9, 1, 7, 2, 3, 8, 0]
The result is not what I expect. In my expectation, when the spaceship operator returns 0, every element would stay in the original position. Since the spaceship operator always returns 0 in the example above, the array should remain as is. However, the result is different from the original array.
Is there something I misunderstand?
Updated:
Following was the idea where my question above came from.
Originally I was trying to order objects by their attributes(Let's assume the attribute is status).
For example
myObjects = [obj1, obj2,..., objn] # the objects are ordered by create_time already
myObjects.sort{|a,b|
case a.status
when 'failed'
x = 1
when 'success'
x = 0
when 'pending'
x = -1
end
case b.status
when 'failed'
y = 1
when 'success'
y = 0
when 'pending'
y = -1
end
x <=> y # compare two objects (a and b) by the status
}
myObjects is ordered by created_time already, but I want to sort it again by each object's status.
For instance,
Two objects with the same created time(only taking hours and minutes into consideration here, just ignoring seconds) will be sorted again by their status, making objects with failed status be put at the end of the array.
The x and y value in the code above will depend on the object's status,
and x y are compared to decide the order. If the statuses of two objects are equal (x == y), the elements should stay in the same position because they are sorted by created_time already, no needs to sort them again.
When the status of two objects are both success, x <=> y will return 0.
But according to some comments, the comparison value 0 returned by the spaceship operator seems to output unpredictable order.
What if myObjects contains elements all with the same status? It would cause the spaceship operator returns 0 all the time since x == y.
In my expectation, myObjects should remain the same order since the statuses are all the same, how should I correct when using the spaceship operator in my case?
Many thanks to everyone's help!
Your assumption about how the sorting works is incorrect. As per the documentation #sort is not stable:
The result is not guaranteed to be stable. When the comparison of two elements returns 0, the order of the elements is unpredictable.
Array#sort uses Quicksort algorithm, which is not stable and can produce this behaviour when elements are "equal".
Reason is in choosing and moving pivot element at every step, ruby implementation seems to choose pivot at middle for this case (but it can be chosen differently).
This is what happens in your example:
pivot is chosen at element 9 at middle of array
now algorithm ensures that items on left of pivot are less than it and items on the right are greater or equal, because all are "equal" - this makes everything to be in right part
now recursively repeat for left(this is always empty in this case) and right partitions
result is sorted_left + [pivot] + sorted_right, left is empty thus pivot is moved
Ruby core documentation mentions this:
When the comparison of two elements returns 0, the order of the elements is unpredictable.
Also spaceship operator <=> does not play any role here, you could just call myArray.sort{0} for the same effect.
Update:
From updated question it's clear that you want to sort by two attributes, this can be done several ways:
Method1: you can invent a metric/key that takes both values into account and sort by it:
status_order = { 'success' => 1, 'failed' => 2, 'pending' => 3 }
myObjects.sort_by{|o| "#{status_order[o.status]}_#{o.created_time}" }
This is not very optimal in terms of extreme performance, but is shorter.
Method2: implicit composite key by writing comparison rule like this:
status_order = { 'success' => 1, 'failed' => 2, 'pending' => 3 }
status_order.default = 0
myObjects.sort{|a,b|
if a.status == b.status
a.created_time <=> b.created_time
else
status_order[a.status] <=> status_order[b.status]
end
}
How it works
myArray = [2, 1, 7, 9, 3, 8, 0]
myArray.sort { |a, b| a <=> b }
#=> [0, 1, 2, 3, 7, 8, 9]
myArray.sort { |a, b| b <=> a }
#=> [9, 8, 7, 3, 2, 1, 0]
If result of comparsion is always 0 it's impossible to sort elements. That is quite logical.
However, the documentation explicitly states that the order of the elements is unpredictable in this case. That's why the result is different from the old array.
However, I replicated your situation in Ruby 2.5.1 and it returns old array
myArray = [2, 1, 7, 9, 3, 8, 0]
myArray.sort { |a, b| 1 <=> 1 }
#=> [2, 1, 7, 9, 3, 8, 0]
There's also a misunderstanding in your code. You wrote
myArray #=> [9, 1, 7, 2, 3, 8, 0]
But in fact Array#sort doesn't change array, only Array#sort! does it.
I am having trouble understanding what seems like an inconsistent behavior in Julia.
X = reshape(1:100, 10, 10)
b = [1 5 9]
X[2, :][b] # returns the correct array
X[2, :][1 5 9] # throws an error
Can someone explain why using the variable b works to index an array but not when I write the index myself?
Since x = X[2,:] is just a vector, we can simplify the example to just talking about indexing behavior on vectors.
x[v] where v is a collection of integers returns the subset of x. Thus x[(1,5,9)], or x[[1,5,9]] is thus using that getindex(x::Vector,i::AbstractArray) dispatch.
Note that x[[1 5 9]] works because v = [1 5 9] makes v a row vector. That's valid syntax, but x[1 5 9] just isn't even valid Julia syntax. That syntax means something else:
v = Float64[1 5 9]
returns a row vector with element type Float64.
I have figured out a solution.
Rather than write X[2, :][1 5 9] I should have written x[2, :][[1 5 9]]
I believe this makes sense when we imagine indexing on two dimensions the second time. This makes it possible to write more complicate indices, like X[2:4, :][[1 3],[1 3]]
Let's say I want to iterate over a numpy array and print each item. I'm going to use this later on to manipulate the (i,j) entry in my array depending on some rules.
I've read the numpy docs and it seems like you can access individual elements in an array easily enough using similar indexing(or slicing) to lists. But it seems that I am unable to do anything with each (i,j) entry when I try to access it in a loop.
row= 3
column = 2
space = np.random.randint(2, size=(row, column))
print space, "\n"
print space[0,1]
print space[1,0] #test if I can access indiivdual elements
output:
[[1,1
[1,1
[0,0]]
1
1
for example, using the above I want to iterate over every row and column and print each entry. I would think to use something like the following:
for i in space[0:row,:]:
for j in space[:,0:column]:
print space[i,j]
the output I get is
[1,1]
[1,1]
[1,1]
[1,1]
[1,1]
[1,1]
[1,1]
[1,1]
[1,1]
Obviously this does not work. I believe the problem is that I'm accessing entire rows and columns instead of elements within any given row and column. I've been going over the numpy docs for a couple of hours and I am still unsure of how to go about this.
My main concern is I want to change each (i,j) entry by using a loop and some conditionals, for example (using the above loop):
for i in space[0:row,:]:
for j in space[:,0:column]:
if [i+1,j] + [i,j+1] == 2:
[i,j] = 1
Start with:
for i in range(row):
for j in range(column):
print space[i,j]
You are generating indices in your loops which index some element then!
The relevant numpy docs on indexing are here.
But it looks, that you should also read up basic python-loops.
Start simple and read some docs and tutorials. After i saw Praveen's comment, i felt a bit bad with this simple answer here which does not offer much more than his comment, but maybe the links above are just what you need.
A general remark on learning numpy by trying:
regularly use arr.shape to check the dimensions
regularly use arr.dtype to check the data-type
So in your case the following should have given you a warning (not a python one; one in your head) as you probably expected i to iterate over values of one dimension:
print((space[0:row,:]).shape)
# output: (3, 2)
There are many ways of iterating over a 2d array:
In [802]: x=np.array([[1,1],[1,0],[0,1]])
In [803]: print(x) # non-iteration
[[1 1]
[1 0]
[0 1]]
by rows:
In [805]: for row in x:
...: print(row)
[1 1]
[1 0]
[0 1]
add enumerate to get an index as well
In [806]: for i, row in enumerate(x):
...: row += i
In [807]: x
Out[807]:
array([[1, 1],
[2, 1],
[2, 3]])
double level iteration:
In [808]: for i, row in enumerate(x):
...: for j, v in enumerate(row):
...: print(i,j,v)
0 0 1
0 1 1
1 0 2
1 1 1
2 0 2
2 1 3
of course you could iterate on ranges:
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i,j]...
for i,j in np.ndindex(x.shape):
print(i,j,x[i,j])
Which is best depends, in part, on whether you need to just use the values, or need to modify them. If modifying you need an understanding of whether the item is mutable or not.
But note that I can remove the +1 without explicit iteration:
In [814]: x-np.arange(3)[:,None]
Out[814]:
array([[1, 1],
[1, 0],
[0, 1]])