I'm trying to find a method to take a particular relation and move it to the end of the array. Basically, I have a current_account and I want to take this account and move it to the end of the account relationship array so that it will display last when I iteration over the relationships. I want to make a scope and use SQL if possible, here is my attempt and I haven't really gotten anywhere.
HTML
<% current_user.accounts.current_sort(current_account).each do |account| %>
<li><%= link_to account.name, switch_account_accounts_path(account_id: account.id) %></li>
<% end %>
This current return a list of sorted by created_at accounts. I don't want it to be sorted by created at but the current_account to be at the bottom so I make a scope called current_sort but I'm not sure what to do here.
CURRENT_SORT SCOPE ON ACCOUNT
scope :current_sort, lambda { |account|
}
I want this scope to return the passed in account last in the association array. How can I do this with SQL or Ruby?
A quick trick to sort a particular element to the end of an array is:
array.sort_by { |v| v == current_account ? 1 : 0 }
If you want to move multiple elements it's easier to do:
to_end = [ a, b ]
array - to_end + to_end
Edit: As Stefan points out, this could potentially re-order items. To fix that:
array.sort_by.with_index do |v, i|
v == current_account ? (array.length + i) : i
end
You can also approach it a different way using partition:
array.partition { |v| v != current_account }.reduce(:+)
Which is a variation on the method used by Stefan in their answer.
You can use partition to split the array by a condition.
array = [1, 2, 3, 4, 5, 6, 7, 8]
current_account = 3
other_accounts, current_accounts = array.partition { |v| v != current_account }
#=> [[1, 2, 4, 5, 6, 7, 8], [3]]
other_accounts
#=> [1, 2, 4, 5, 6, 7, 8]
current_accounts
#=> [3]
The results can be concatenated:
other_accounts + current_accounts
#=> [1, 2, 4, 5, 6, 7, 8, 3]
or in a single line:
array.partition { |v| v != current_account }.flatten(1)
#=> [1, 2, 4, 5, 6, 7, 8, 3]
# or
array.partition { |v| v != current_account }.inject(:+)
#=> [1, 2, 4, 5, 6, 7, 8, 3]
Related
Suppose that you need to get all the elements that have the max value in an array.
A possible method would be to sort the array then use Enumerable#take_while:
array = [ 1, 3, 2, 3, 2, 3 ].sort {|a,b| b - a}
array.take_while { |e| e == array[0] }
#=> [3, 3, 3]
Now, when you are beautifully chaining methods and don't want to stop the chain just for storing the sorted array (which you'll need for referencing its first element in the take_while block), how would you do it?
I posted the question and an answer below for reference, but I probably missed better ways, so feel free to post your own method
Another way:
arr = [ 1, 3, 2, 3, 2, 3 ]
arr.sort {|a,b| b - a}.tap { |a| a.select! { |e| e == a.first } }
#=> [3, 3, 3]
Note that arr is not mutated.
ruby < 2.5
My original response to the question: sort.slice_when.first
[ 1, 3, 2, 3, 2, 3 ].sort {|a,b| b - a}.slice_when {|a,b| b != a}.first
#=> [3, 3, 3]
note: As slice_when returns an Enumerator, this solution won't walk through all the sorted array when chaining it with first. There is a more performant solution below tough.
ruby >= 2.5
Combining #engineersmnky and #Cary methods: then and max+select
[ 1, 3, 2, 3, 2, 3 ].then { |arr| mx = arr.max; arr.select { |elm| elm == mx } }
#=> [3, 3, 3]
You can try this
pry(main)> [ 1, 2, 2, 3, 3, 3 ].sort.slice_when {|a,b| b > a}.to_a.last
=> [3, 3, 3]
A bit similar of the last solution but also different.
Source https://ruby-doc.org/core-3.0.2/Enumerable.html#method-i-slice_when
I have this array
[1, 2, 3, 4, 5, 6]
I would like to get the first 2 elements that are bigger than 3.
I can do:
elements = []
[1, 2, 3, 4, 5, 6].each do |element|
elements << element if element > 3
break if elements.size == 2
end
puts elements
Is there a more elegant way to do this?
Is there something in the Ruby core like Array.select(num_elements, &block)?
You were nearly there. Just use break with a parameter:
[1, 2, 3, 4, 5, 6].each_with_object([]) do |element, acc|
acc << element if element > 3
break acc if acc.size >= 2
end
Another way to accomplish it, would be to use Enumerator::Lazy with array.lazy.select, or an explicit Enumerator instance with Enumerable#take (here it’s a definite overkill, posting mostly for educational purposes.)
enum =
Enumerator.new do |y|
i = [1, 2, 3, 4, 5, 6].each
loop { i.next.tap { |e| y << e if e > 3 } }
end
enum.take(2)
#⇒ [4, 5]
Sidenote: both examples above would stop traversing the input as soon as two elements are found.
a = [1, 2, 3, 4, 5, 6]
p a.filter {|x| x > 3}.first(2)
Or
p a.select{|x| x > 3}.first(2)
output
[4, 5]
As Cary suggest, the given below code wouldn't be a performance hit if array is bigger, it would stop executing further if 2 elements are found
a.lazy.select{|x| x > 3}.first(2)
Just for having a couple of options more..
ary.each_with_object([]) { |e, res| res << e if e > 3 && res.size < 2 }
or
ary.partition { |e| e > 3 }.first.first(2)
In Ruby, let's say I have an array of ordreed, unique numbers
[0, 1, 2, 4, 6, 8, 10]
If the first element of the array is zero, how do I remove all the elements from teh beginning of the array that are consecutive, starting wiht zero? That is, in the above example, I would want to remove "0", "1", and "2" leaving me with
[4, 6, 8, 10]
But if my array is
[1, 2, 3, 10, 15]
I would expect the array to be unchanged because the first element is not zero.
You could use a mix of drop_while and with_index to only remove the first matching elements:
[0, 1, 2, 4, 6, 8, 10].drop_while.with_index{|x, i| x == i}
# [4, 6, 8, 10]
[1, 1, 2, 4, 6, 8, 10].drop_while.with_index{|x, i| x == i}
# [1, 1, 2, 4, 6, 8, 10]
Note that the second and third elements don't get deleted in the second example, even though they're equal to their indices.
Drop elements, as long as they are equal to their index:
a=a.drop_while.with_index{|e,i| e==i}
Sounds like you're trying to delete entities if they match their idx (provided the first idx is 0). Try this:
if array.first == 0
new_array = array.reject.each_with_index{ |item, idx| item == idx }
end
Although this will only work with ordered arrays of unique numbers, if you're not sure that they are then include: array = array.sort.uniq
You could do:
x = -1
while my_array.first == x + 1 do
x = my_array.shift
end
Note that array.shift is the same as array.pop except that it works from the start of the array.
If I understand you right, then it can be one of possible solutions:
def foo(array)
if array.first.zero?
array.keep_if.with_index { |e, ind| e != ind }
else
array
end
end
> foo([0, 1, 2, 5, 6, 7])
#=> => [5, 6, 7]
> foo([1, 2, 3])
#=> [1, 2, 3]
In short form:
a[0] == 0 ? a[3..-1] : a
In longer form:
if a.first == 0
a[3..(a.size)]
else
a
end
I have an array #ary = [1, 3, 4, 2, 7, 8, 9] and I want to know how many possibilities of combination can add equal to 9.
I should have four possibilities can add equal to 9 [1,8]、[2, 3, 4]、[9]、[2, 7],but in my code, I just can know two of possibilities and just can show one possibility in this problem.
def sums (num, target)
random1 = num.sample
random2 = num.sample
if random1+random2 == target
ary1 = [random1, random2]
end
end
If you're interested in the combinations themselves as opposed to just the count:
(1..a.size).flat_map { |n| a.combination(n).to_a }
.keep_if { |c| c.inject(:+) == 9 }
#=> [[9], [1, 8], [2, 7], [3, 4, 2]]
You can use Array#combination:
(1..ary.size).inject(0) do |a, e|
a + ary.combination(e).count { |e| e.sum == 9 }
end
#=> 4
You can use inject(:+) instead of sum if your ruby version is lower than 2.4.
Let's take the following Array:
[1, 4, 5, 3, 1, 4, 6, 5, 4]
It has the following turning points (when rise changes to fall, or vice versa):
5 (at index 2)
1 (at index 4)
6 (at index 6)
To make task more general:
There is an Array a = [a1, a2, ...]
There is function p(x,y) -> z, where z is Comparable
How to get all elements ai ∈ a (0 < i < a.length-1) for which p(ai-1, ai) != p(ai, ai+1)
I would like to write something like:
a.detect_edges{|prev, n| prev >= n} # => [[5,2], [1, 4], [6,6]]
What's the most elegant way to get those turning points with their respective indexes? Here's my code with which I'm not satisfied from the aesthetic point of view:
class Array
def detect_edges(&blk)
return nil if self.length < 2
prev = blk.call(self[0], self[1])
result = []
self[0..-2].each_with_index do |elem, i|
current = blk.call(elem, self[i+1])
if current != prev
result.push [elem, i]
end
prev = current
end
result
end
end
[1, 4, 5, 3, 1, 4, 6, 5, 4]
.each_cons(3).with_index(1)
.reject{|(e1, e2, e3), i| (e1 <=> e2) == (e2 <=> e3)}
.map{|(e1, e2, e3), i| [e2, i]}
# => [[5, 2], [1, 4], [6, 6]]
Look ma, no map!
a = [1, 4, 5, 3, 1, 4, 6, 5, 4]
a[1..-2].each.with_index(1).reject { |e,i| (a[i-1]<=>e) == e<=>a[i+1] }
#=> [[5, 2], [1, 4], [6, 6]]
So you basically want the elements and their indices, where the element is the local max in a 1 index range:
arr.each.with_index.select { |element, index| element == arr[index.pred..index.next].max }
# => [[5, 2], [6, 6]]
Note, you have to handle the case for the first element or if elements are equal.
EDIT: for your updated version, you just have to check if the result of <=> has changed. Note that you will again have to check the case when elements are equal:
arr.each.with_index.to_a.tap(&:pop).drop(1).select do |element, index|
(arr[index.pred] <=> element) != (element <=> arr[index.next])
end # => [[5, 2], [1, 4], [6, 6]]
I don't see reason to get more fancy than:
class Array
def detect_edges
self.collect.with_index do |e, i|
next if i == 0 || i >= size-1
yield(self[i-1],e) != yield(e,self[i+1]) ? [e, i] : nil
end.compact
end
end
Note that when patching Array one should probably use refinements.