I want to know how we can identify that descending order Range is given.
I have a test case where range is given in (1..-1) and to_a method gives empty array for same.
(1..5).to_a
=> [1, 2, 3, 4, 5]
(5..1).to_a
=> []
Help me write a condition which can confirm descending order Range is given.
Use Range#size:
(5..1).size.zero?
#⇒ true
(1..5).size.zero?
#⇒ false
Range has Range#first and Range#last methods.
r = (5..1)
r.first > r.last # => true
If you want just to check for a reverse range:
(5..1).to_a.empty? #=> true
(1..5).to_a.empty? #=> false
If you want to have an array as output, you could do
r.to_a.then { |ary| ary.empty? ? (r.end..r.begin).to_a.reverse : ary }
The output for the two cases are:
r = (5..1) #=> [5, 4, 3, 2, 1]
r = (1..5) #=> [1, 2, 3, 4, 5]
(1..5).to_a.present? ? "Ascending" : "Descending"
if value present in array means it Ascending or Descending cause if you give (5..1) range it create blank Array.
Ok so I have an array of 'winner ids' this array represents users who have won in the previous round of a tournament. I then have an array of objects called 'tournament participations', this represents all the users that have participated in the tournament (many to many relationship join table).
For each 'winner id' I want to iterate through the 'tournament participations' array and find the tournament participation with a user_id that matches the 'winner id', then push it into a new array called 'round participations'...
I have tried the code below but I always get returned the original 'winner_ids' array....
#challenges = Challenge.where(tournament_id: #tournament.id)
#winner_ids = #challenges.pluck(:winner_id)
#tournament_participations = #tournament.tournament_participations
#round_participations = []
#round_participations = #winner_ids.each do |winner_id|
#round_participation = #tournament_participations.where(user_id: winner_id)
#round_participations << #round_participation
end
each returns the enumerable it was called on; in this case, #winner_ids.each is returning #winner.ids. You don't need to assign the result of the iteration to #round_participations.
Also, check out the map method.
Use the map method, instead of .each.
The map method can be used to create a new array based on the original array, but with the values modified by the supplied block. See the below example.
In case of each method
irb(main):001:0> arr = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):002:0> arr.each { |a| print a -= 10, " " }
-9 -8 -7 -6 -5
=> [1, 2, 3, 4, 5]
you can see after iterations it returned the original array. But in the case of map
irb(main):005:0> arr = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):006:0> arr.map { |a| 2*a }
=> [2, 4, 6, 8, 10]
map can transform the contents of an array, meaning that it can perform an operation on each element in the array.
OK, so I have this array of arrays. Each array within the larger array is very much the same, ten specific values. If my value at location 3 is a specific value, then I want to iterate through the rest of the remaining arrays within the larger array and see if the first 3 values at locations 0, 1, and 2 match. if they then match, I'd like to delete the original array. I'm having a hard time with it, maybe there is an easy way? I'm sure there is, I'm fairly new to this whole coding stuff =) So much appreciation in advance for your help....
here's where I'm at:
#projectsandtrials.each do |removed|
if removed[3] == ["Not Harvested"]
#arraysforloop = #projectsandtrials.clone
#arraysforloop1 = #arraysforloop.clone.delete(removed)
#arraysforloop1.each do |m|
if (m & [removed[0], removed[1], removed[2]]).any?
#projectsandtrials.delete(removed)
end
end
end
end
Lets look at your situation:
#projectsandtrials.each do |removed|
// some logic, yada yada
#projectsandtrials.delete(removed)
end
You can't just delete stuff out of an array you're iterating through. At least not until you finish iterating through it. What you should be using instead is a filtering method like reject instead of just an each.
So instead of deleting right there, you should just return true when using reject.
I think about it like this when iterating through arrays.
Do I want the array to stay the same size and have the same content?
Use each.
Do I want the array to be the same size, but have different content?
Use map.
Do I want the array to be less than or equal to the current size?
Use select or reject.
Do I want it to end up being a single value?
Use reduce.
Code
def prune(arr, val)
arr.values_at(*(0..arr.size-4).reject { |i| arr[i][3] == val &&
arr[i+1..i+3].transpose[0,3].map(&:uniq).all? { |a| a.size==1 } }.
concat((arr.size-3..arr.size-1).to_a))
end
Example
arr = [ [1,2,3,4,0],
[3,4,5,6,1],
[3,4,5,4,2],
[3,4,5,6,3],
[3,4,5,6,4],
[3,4,0,6,5],
[2,3,5,4,6],
[2,3,5,5,7],
[2,3,5,7,8],
[2,3,5,8,9],
[2,3,5,7,0]
]
Notice that the last values of the elements (arrays) of arr are consecutive. This is to help you identify the elements of prune(arr, 4) (below) that have been dropped.
prune(arr, 4)
# => [[3, 4, 5, 6, 1],
# [3, 4, 5, 4, 2],
# [3, 4, 5, 6, 3],
# [3, 4, 5, 6, 4],
# [3, 4, 0, 6, 5],
# [2, 3, 5, 5, 7],
# [2, 3, 5, 7, 8],
# [2, 3, 5, 8, 9],
# [2, 3, 5, 7, 0]]
Explanation
The arrays at indices 0 and 6 have not been included in array returned.
arr[0] ([1,2,3,4,0]) has not been included because arr[0][3] = val = 4 and arr[1], arr[2] and arr[3] all begin [3,4,5].
arr[6] ([2,3,5,4,6]) has not been included because arr[6][3] = 4 and arr[7], arr[8] and arr[9] all begin [2,3,5].
arr[2] ([3,4,5,5,2]) has been included because, while arr[2][3] = 4, arr[3][0,3], arr[4][0,3] and arr[5][0,3] all not all equal (i.e., arr[5][2] = 0).
Notice that the last three elements of arr will always be included in the array returned.
Now let's examine the calculations. First consider the following.
arr.size
#=> 11
a = (0..arr.size-4).reject { |i| arr[i][3] == val &&
arr[i+1..i+3].transpose[0,3].map(&:uniq).all? { |a| a.size==1 } }
#=> (0..7).reject { |i| arr[i][3] == val &&
arr[i+1..i+3].transpose[0,3].map(&:uniq).all? { |a| a.size==1 } }
#=> [1, 2, 3, 4, 5, 7]
Consider reject's block calculation for i=0 (recall val=4).
arr[i][3] == val && arr[i+1..i+3].transpose[0,3].map(&:uniq).all? {|a| a.size==1 }}
#=> 4 == 4 && arr[1..3].transpose[0,3].map(&:uniq).all? { |a| a.size==1 }
#=> [[3,4,5,6,1],
# [3,4,5,4,2],
# [3,4,5,6,3]].transpose[0,3].map(&:uniq).all? { |a| a.size==1 }
#=> [[3, 3, 3],
# [4, 4, 4],
# [5, 5, 5],
# [6, 4, 6],
# [1, 2, 3]][0,3].map(&:uniq).all? { |a| a.size==1 }
#=> [[3, 3, 3],
# [4, 4, 4],
# [5, 5, 5]].map(&:uniq).all? { |a| a.size==1 }
#=> [[3], [4], [5]].all? { |a| a.size==1 }
#=> true
meaning arr[0] is to be rejected; i.e., not included in the returned array.
The remaining block calculations (for i=1,...,10) are similar.
We have computed
a #=> [1, 2, 3, 4, 5, 7]
which are the indices of all elements of arr except the last 3 that are to be retained. To a we add the indices of the last three elements of arr.
b = a.concat((arr.size-3..arr.size-1).to_a)
#=> a.concat((8..10).to_a)
#=> a.concat([8,9,10])
#=> [1, 2, 3, 4, 5, 7, 8, 9, 10]
Lastly,
arr.values_at(*b)
returns the array given in the example.
Your code snippet seems fine, although there are couple of things to note:
#arraysforloop.clone.delete(removed) removes all the occurences of removed array (not only the first one). E.g. [1,2,3,1].delete(1) would leave you with [2,3]. You could fix it with using an iterator for #projectsandtrials and delete_at method.
delete method returns the same argument you pass to it (or nil if no matches found). So #arraysforloop1 = #arraysforloop.clone.delete(removed) makes your #arraysforloop1 to contain the removed array's elements only! Removing an assignment could save you.
I see no reason to have two cloned arrays, #arraysforloop and #arraysforloop1, as the former one is not used anyhow later. May be we could omit one of them?
#projectsandtrials.delete(removed) leaves you in a strange state, as long as you're iterating the same array right now. This could end up with you missing the right next element after the removed one. Here is a simple snippet to illustrate the behaviour:
> a = [1,2,3]
> a.each{|e, index| puts("element is: #{e}"); a.delete(1);}
element is: 1
element is: 3
As you see, after deleting element 1 the loop moved to element 3 directly, omitting the 2 (as it became the first element in array and algorithm thinks it's been handled already).
One of the possibilities to make it less messy is to split it to a bundle of methods. Here is an option:
def has_searched_element? row
# I leave this method implementation to you
end
def next_rows_contain_three_duplicates?(elements, index)
# I leave this method implementation to you
end
def find_row_ids_to_remove elements
[].tap do |result|
elements.each_with_index do |row, index|
condition = has_searched_element?(row) && next_rows_contain_three_duplicates?(elements, index)
result << index if condition
end
end
end
row_ids_to_remove = find_row_ids_to_remove(#projectsandtrials)
# now remove all the elements at those ids out of #projectsandtrials
I'm coding a plugin in Google Sketchup with ruby and I faced a real problem while trying to permute two arrays that are present in an array all this depending on a user combination.
I have an array of arrays like [["1"],["lol"], ["so"]]
When we have a combination like this <[1,
2, 3] it's fine, it should stay the same : [["1"],["lol"], ["so"]]
But when we have a combination like this [2, 3, 1], the output should be : [["lol"], ["so"], ["1"]]
For [3,1,2] => [["so"], ["1"], ["lol"]]
...etc
EDIT
Sorry guys I forgot for the array I have a bit like : [["1, 2, 3"], ["lol1, lol2, lol3"], ["so1, so2, so3"]] so for the combination [2, 3, 1] the output should be : [["2, 3, 1"], ["lol2, lol3, lol1"], ["so2, so3, so1"]]
Thanks for helping me out.
You could use collect:
array = [["1"],["lol"], ["so"]]
indexes = [2, 1, 3]
indexes.collect {|i| array[i-1]} #=> [["lol"], ["1"], ["so"]]
If you set the indexes to be 0-based you could drop the -1
split and map can be used to turn your strings into values:
"1, 2, 3".split(",").map { |i| i.to_i} # [1, 2, 3]
You can then also split your strings
"lol2, lol3, lol1".split(/, /) #=> ["lol2", "lol3", "lol1"]
You should be able to put that together with the above to get what you want.
indexes = [2, 1, 3]
array = [["1"],["lol"], ["so"]]
result = indexes.map{|index| array[index-1] }
You should also take a look at active_enum
https://github.com/adzap/active_enum
You could do something like:
class YourClassName < ActiveEnum::Base
value [1] => ['1']
value [2] => ['lol']
value [3] => ['so']
end
a = [["1"], ["lol"], ["so"]]
index = [2, 1, 3]
index.collect {|i| a[i - 1]}
This outputs
[["lol"], ["1"], ["so"]]
I have an array of objects, type Lab, existing_labs, and a different array of Labs, new_labs. I have defined my own == comparator for Lab.
existing_labs is Labs that already exist in the db.
new_labs is Labs to be saved, based on their existence in the db already.
Right now I am doing this
new_labs.each do |l|
lab = Lab.new(l)
exists = existing_labs.map{ |existing_lab| lab == existing_lab }
lab.save unless exists.include? true
end
This works, but can be slow if either or both of the arrays is large. I'm sure there has to be a better way to do this!
Edit for clarity:
My question is: Is there a good way to return all of the Lab that are present in new_labs that are not present in existing_labs, using my == method?
new_labs.each do |l|
lab = Lab.new(l)
lab.save unless existing_labs.any?{|existing_lab| lab == existing_lab }
end
Should be somewhat faster, because no array of trues and falses is created for each lab. Also, any? quits when one true is found.
Your question is not at all clear, but here is a refactorization of your code:
(new_labs.map{|l| Lab.new(l)} - existing_labs).each(&:save)
Make a set out of them and apply the difference method.
returns the elments in array1 that are not in array2
> ary1 = [1, 2, 3, 4, 5]
> ary2 = [2,7,9,3]
> ary1 - ary2
=> [1, 4, 5]
returns the elments in array2 that are not in array1
> ary1 = [1, 2, 3, 4, 5]
> ary2 = [2,7,9,3]
> ary2 - ary1
=> [7,9]
You can simply use the - method:
> ary1 = [1, 2, 3, 4, 5]
> ary2 = [2,3]
> ary1 - ary2
=> [1, 4, 5]
Another one:
ary1.select { |e| not ary2.include? e }
Any of these methods imply iteration on the background, but I think the - method wins on performance, maybe a couple of benchmarks would be good.