ruby - Permutation between elements of an array - ruby-on-rails

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"]]

Related

Create list of lists from list of json objects ruby

I have a list of json objects something like this:
test = [{"a": 1, "b": 2, "c": 3}, {"a": 4, "b": 5, "c":6}]
I want to fetch the fields 'a' and 'c' from the above test list to create a list of list something like this:
[[1, 4], [3, 6]]
The idea is to make a list of all the values of a, then the values of c.
When I am using pluck:
test.pluck(:a, :c)
I am getting output like this:
[[1, 3], [4, 6]]
One approach I tried which is working fine.
res = []
res << test.pluck(:a)
res << test.pluck(:c)
But I am thinking it would be better if I get one or two liner solution,
with or without inbuilt function because the number of fields in the future may increase.
You were looking for following,
%i(a c).map { |x| test.map { |e| e[x] } }
You could use Array#transpose on the pluck result. Which assumes the array represents rows or columns and swaps the representation around.
test.pluck(:a, :c).transpose
#=> [[1, 4], [3, 6]]

Identifying ascending and descending order or Range

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.

Iterating Over 2 Arrays And Building A New Array From Matches

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.

ruby uniq on the first two columns of two dimensional array

I have a two dimensional array with three fields in the second dimension.
Is it possible to use uniq on the first two fields of the second dimension?
I have seen array.uniq! {|c| c.first}. When I am correct, this apply uniq on the first field of the array.
Is it possible to use something like array.uniq! {|c| c.first c.second}?
#array = Array.new()
#array << Array.new([journal.from_account_number, journal.from_account,
journal.buchungsart])
There are several entries in #array.
The question was how to get unique values from array not considering journal.buchungsart.
The answer was: #array = #array.uniq! {|c| [c.first, c.second]}
Yes, put the values in an array:
array.uniq! {|c| [c.first, c.second]}
Just return an array of two elements in the block.
Since Array#second is not defined in the standard library, do
array.uniq! { |c| [c[0], c[1]] }
instead, which can be further simplified to array.uniq! { |c| c[0..1] }
You could also make use of the fact that hash keys are unique.
arr = [[1, 2, 3],
[2, 1, 4],
[1, 2, 5]]
a = arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) }.values
#=> [[1, 2, 5], [2, 1, 4]]
See Hash#update (aka merge!).
Before extracting the hash values with Hash#value, we have computed the following hash.
arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) }
#=> {[1, 2]=>[1, 2, 5], [2, 1]=>[2, 1, 4]}
Notice that, for given values of the first two elements in a row, it is the last row of arr with those values that is to be "kept". The first such row in arr is to be kept, use the following.
arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) { |_,o,_| o } }.values
#=> [[1, 2, 3], [2, 1, 4]]
This does not mutate arr. If arr is to be modified, write
arr.replace(a)
where a is defined above.
Array#first accepts a parameter :
%w(a b c d e f).first(2)
# => ["a", "b"]
so you could just use :
array.uniq!{ |c| c.first(2) }

remove array from multidimensional array if first 3 values aren't unique in ruby on rails

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

Resources