Converting an array to hash in ruby - arrays

Supposed I have an array that looks like
testarr = [["Actor", "Morgan", "33", ["A","B"]],
["Movie", "Titanic", "44", ["A","A"]],
["Actor", "Jack Black", "333", ["A","A"]]]
I want to convert this into a hash which will be converted into a json eventually.
I want it to look like
{
"Actor" => {
{ "name" : "Morgan",
"Age" : 33",
"Films: { "A", "B" }} ,
{ "name" : "Jack Black",
"Age" : 44",
"Films: { "A", "A" }}
}
"Movie" => {
{ "Title" : "Titanic"
"Gross" : "44"
"Actors" : { "A", "A" }
}
}
Not sure about the exact format, but whatever makes sense.
I tried
def hashing(arr)
hash = Hash.new
arr.each do |item|
if item[0] == "Movie"
item.delete("Movie")
hash["Movie"] = item
item["Title"] = item[1]
item["Movie"]["Box Office"] = item[2]
item["Movie"]["Actors"] = item[3]
else
item.delete("Actor")
hash["Actor"] = item
item["Actor"]["Name"] == item[1]
item["Actor"]["Age"] == item[2]
item["Actor"]["Filmography"] == item[3]
end
end
return hash
end
testarr = [["Actor", "Morgan", "33", ["dsfds","dsfdsf"]],
["Movie", "Titanic", "44", ["dsfds","dfdsf"]],
["Actor", "Jack Black", "333", ["ssdsfds","dsfdsf"]]]
puts hashing(testarr)
But it gives me an error for putting the array item into "Movie" and "Actor" then trying to create keys like "Name" and "Age".
How can I make this as I desire?

testarr = [["Actor", "Morgan", "33", ["A","B"]],
["Movie", "Titanic", "44", ["A","A"]],
["Actor", "Jack Black", "333", ["A","A"]]]
a = Hash.new{ |h,k| h[k] = [] }
testarr.each do |arr|
b = {name: arr[1], age: arr[2], films: arr[3]}
a[arr[0]] << b
end
this will produce
{"Actor"=>[{"name"=>"Morgan", "age"=>"33", "films"=>["A", "B"]}, {"name"=>"Jack Black", "age"=>"333", "films"=>["A", "A"]}], "Movie"=>[{"name"=>"Titanic", "age"=>"44", "films"=>["A", "A"]}]}

Please try the below code,
v = [["Actor", "Morgan", "33", ["A", "B"]], ["Movie", "Titanic", "44", ["A", "A"]], ["Actor", "Jack Black", "333", ["A", "A"]]]
v.inject({}) do |ot, arr|
item = {name: arr[1], age: arr[2], films: arr[3]}
if ot[arr[0]].present?
ot[arr[0]] << item
else
ot[arr[0]] = []
ot[arr[0]] << item
end
ot
end
And the o/p is like below,
# => {"Actor"=>[{:name=>"Morgan", :age=>"33", :films=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :films=>["A", "A"]}], "Movie"=>[{:name=>"Titanic", :age=>"44", :films=>["A", "A"]}]}
Please note here the Actor is not hash of hashes, it's array of hashes, this is the standard way of keeping collection and convert it to json if you need by using to_json method.

You need to iterate through the array and parse each item, appending it to the resultant hash.
testarr = [["Actor", "Morgan", "33", ["A", "B"]],
["Movie", "Titanic", "44", ["A", "A"]],
["Actor", "Jack Black", "333", ["A", "A"]]]
results = {}
testarr.each do |item|
key, a, b, c = item
r = if key == 'Actor'
{ name: a, age: b, movies: c }
elsif key == 'Movie'
{ title: a, gross: b, actors: c }
end
results[key] = [] unless results[key]
results[key] << r
end
puts results
This will produce:
{"Actor"=>[{:name=>"Morgan", :age=>"33", :movies=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :movies=>["A", "A"]}], "Movie"=>[{:title=>"Titanic", :gross=>"44", :actors=>["A", "A"]}]}

The value in your :actor contains a hash without a key. The best thing you can do is put that into an array.
This will work. There might be a cleaner way, but I'm not sure how at the moment:
h = Hash.new { |hash, key| hash[key] = [] }
testarr = [["Actor", "Morgan", "33", ["A", "B"]], ["Movie", "Titanic", "44", ["A", "A"]], ["Actor", "Jack Black", "333", ["A", "A"]]]
testarr.each do |t|
if t[0] == 'Movie'
h[t[0]] << {title: t[1], gross: t[2], actors: t[3]}
else
h[t[0]] << {name: t[1], age: t[2], films: t[3]}
end
end
puts h
Output:
{"Actor"=>[{:name=>"Morgan", :age=>"33", :films=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :films=>["A", "A"]}], "Movie"=>[{:title=>"Titanic", :gross=>"44", :actors=>["A", "A"]}]}

I tried to keep the example you wrote.
First of all, it must be shaped for Array(such as [a, b] ) not Hash( {a, b} ) list of items
# You may want result like this ...
{
"Actor": [ # not '{' but '['
{
"name": "Morgan",
"Age": "33",
"Films": ["A", "B"] # not '{' but '[' also
},
{
"name": "Jack Black",
"Age": "44",
"Films": ["A", "A"]
}
],
"Movie": [
{
"Title": "Titanic",
"Gross": "44",
"Actors": ["A", "A"]
}
]
}
and then your function should be like this ...
def hashing(arr)
hash = Hash.new
hash["Movie"], hash["Actor"] = [], []
arr.each do |item|
if item[0] == "Movie"
movie = {}
movie["Title"] = item[1]
movie["Box Office"] = item[2]
movie["Actors"] = item[3]
item.delete("Movie") # optional
hash["Movie"] << movie
else
actor = {}
actor["Name"] = item[1]
actor["Age"] = item[2]
actor["Filmography"] = item[3]
item.delete("Actor") # optional
hash["Actor"] << actor
end
end
return hash
end
Then it's time to test!
as your codes,
testarr = [
["Actor", "Morgan", "33", ["dsfds","dsfdsf"]],
["Movie", "Titanic", "44", ["dsfds","dfdsf"]],
["Actor", "Jack Black", "333", ["ssdsfds","dsfdsf"]]
]
puts hashing(testarr)
It will return this:
{
"Movie"=>
[
{"Title"=>"Titanic", "Box Office"=>"44", "Actors"=>["dsfds", "dfdsf"]}
],
"Actor"=>
[
{"Name"=>"Morgan", "Age"=>"33", "Filmography"=>["dsfds", "dsfdsf"]},
{"Name"=>"Jack Black", "Age"=>"333", "Filmography"=>["ssdsfds", "dsfdsf"]}
]
}

Code
def convert(arr, keys)
arr.group_by(&:first).transform_values do |a|
a.map { |key, *values| keys[key].zip(values).to_h }
end
end
Example (using testarr defined in the question)
keys = { "Actor"=>[:name, :Age, :Films], "Movie"=>[:Title, :Gross, :Actors] }
convert(testarr, keys)
#=> { "Actor"=>[
# {:name=>"Morgan", :Age=>"33", :Films=>["A", "B"]},
# {:name=>"Jack Black", :Age=>"333", :Films=>["A", "A"]}
# ],
# "Movie"=>[
# {:Title=>"Titanic", :Gross=>"44", :Actors=>["A", "A"]}
# ]
# }
Explanation
See Enumerable#group_by, Hash#transform_values, Array#zip and Array#to_h.
The steps are as follows.
h = testarr.group_by(&:first)
#=> { "Actor"=>[
# ["Actor", "Morgan", "33", ["A", "B"]],
# ["Actor", "Jack Black", "333", ["A", "A"]]
# ],
# "Movie"=>[
# ["Movie", "Titanic", "44", ["A", "A"]]
# ]
# }
Though not quite equivalent, you can think of testarr.group_by(&:first) as being "shorthand" for testarr.group_by { |a| a.first }. Continuing,
e0 = h.transform_values
#=> #<Enumerator:
# {"Actor"=>[["Actor", "Morgan", "33", ["A", "B"]],
# ["Actor", "Jack Black", "333", ["A", "A"]]],
# "Movie"=>[["Movie", "Titanic", "44", ["A", "A"]]]}
# :transform_values>
The first element is generated by the enumerator e0, passed to the block and the block variable is set equal to that value.
a = e0.next
#=> [["Actor", "Morgan", "33", ["A", "B"]],
# ["Actor", "Jack Black", "333", ["A", "A"]]]
A second enumerator is now created.
e1 = a.map
#=> #<Enumerator: [["Actor", "Morgan", "33", ["A", "B"]],
# ["Actor", "Jack Black", "333", ["A", "A"]]]:map>
The first value is generated by e1, passed to the inner block and the block variables are assigned values (using disambiguation).
key, *values = e1.next
#=> ["Actor", "Morgan", "33", ["A", "B"]]
key
#=> "Actor"
values
#=> ["Morgan", "33", ["A", "B"]]
The inner block calculation is now performed.
b = keys[key].zip(values)
#=> keys["Actor"].zip(["Morgan", "33", ["A", "B"]])
#=> [:name, :Age, :Films].zip(["Morgan", "33", ["A", "B"]])
#=> [[:name, "Morgan"], [:Age, "33"], [:Films, ["A", "B"]]]
b.to_h
#=> {:name=>"Morgan", :Age=>"33", :Films=>["A", "B"]}
Now the second and last element is generated by e1 and the same calculations are performed.
key, *values = e1.next
#=> ["Actor", "Jack Black", "333", ["A", "A"]]
b = keys[key].zip(values)
#=> [[:name, "Jack Black"], [:Age, "333"], [:Films, ["A", "A"]]]
b.to_h
#=> {:name=>"Jack Black", :Age=>"333", :Films=>["A", "A"]}
When another value is sought from e1 we obtain the following.
e1.next
#=> StopIteration: iteration reached an end
This exception is caught, causing e1 to return to the outer block. At this point e0 generates it next (and last value).
a = e0.next
#=> [["Movie", "Titanic", "44", ["A", "A"]]]
The remaining calculations are similar.

Related

How to reject an element from an array which also contains hashes, in Ruby?

Eg:
hash_var = ["1", "2", {"a" => ["aa", "bb", "cc"]}]
reject(hash_var, "1") # should return ["2", {"a" => ["aa", "bb", "cc"]}]
reject(hash_var, "a") # should return ["1", "2"]
reject(hash_var, {"a" => ["aa"]}) # should return ["2", {"a" => ["bb", "cc"]}]
Basically, I should be able to reject the element from the array, be it hash or array element.
you can use delete mehod on hash like
hash_var = ["1", "2", {"a" => ["aa", "bb", "cc"]}]
hash_var.delete("1") //this will delete 1 from hash then new value of hash will be ["2", {"a"=>["aa", "bb", "cc"]}]
hash_var[1]["a"].delete("aa") //this will give output ["2", {"a"=>["bb", "cc"]}]
and so on according to your use
That should do the job
hash_var.drop_while { |e| e == '1' }
hash_var.reject { |e| e == '1' }
hash_var.find_all { |e| e != '1' }
hash_var.select { |e| e != '1' }
You could define the instance method Enumerable#dig_drop:
arr = ["1", "2", {"a" => ["aa", "bb", "cc"]}, {"b" => ["aa", "bb"]}]
module Enumerable
def dig_drop(*elm)
self.each_with_index {|_, idx|
unless self[idx].respond_to? :dig
self.delete(elm[0])
next
end
last=elm.pop
if elm.empty?
self[idx].delete(last)
else
self[idx].dig(*elm).nil? ? nil : self[idx].dig(*elm).delete(last)
end
}
self.reject!(&:empty?)
end
end
arr.dig_drop "1" # arr equals ["2", {"a" => ["aa", "bb", "cc"]}, {"b" => ["aa", "bb"]}]
arr.dig_drop('a') # arr equals ["2", {"b" => ["aa", "bb"]}]
arr.dig_drop('b', 'bb') # arr equals ["2", {"b" => ["aa"]}
I think this method should work:
def reject(hash_var, ele)
# Find the index of the ele
# If it is found, just remove it and return hash_var
idx = hash_var.index(ele)
if idx
hash_var.delete_at(idx)
return hash_var
end
# Convert the hashes to arrays - 2D arrays
# { "b" => "2" } becomes [["b", "2"]]
(0...hash_var.length).each do |n|
if hash_var[n].class == Hash
hash_var[n] = hash_var[n].to_a
end
end
(0...hash_var.length).each_with_index do |n, i|
# Only operate if hash_var[n] is an array
# this means that this is 2D array i.e. hash
if hash_var[n].class == Array
# If this is just as element, delete the whole hash_key
# i.e. reject(hash_var, "a")
# here ele = "a", so since this is not hash
# we need to delete full element i.e. ["aa", "bb", "cc"]
if hash_var[n][0][0] == ele
hash_var.delete_at(i)
else
next if ele.class == String
ele_a = ele.to_a
if hash_var[n][0][0] == ele_a[0][0]
diff = hash_var[n][0][1] - ele_a[0][1]
hash_var[n][0][1] = diff
end
end
end
end
## Convert the 2D arrays back to hashes
(0...hash_var.length).each do |n|
if hash_var[n].class == Array
hash_var[n] = hash_var[n].to_h
end
end
# return final
return hash_var
end
Tests:
hash_var = ["1", "2", {"a" => ["aa", "bb", "cc"]}]
p reject(hash_var, "1")
# => ["2", {"a"=>["aa", "bb", "cc"]}]
p reject(hash_var, "a")
# => ["1", "2"]
p reject(hash_var, {"a" => ["aa"]})
# => ["1", "2", {"a"=>["bb", "cc"]}]

How to check if hash keys match value from array

I have:
arr = ['test', 'testing', 'test123']
ht = {"test": "abc", "water": "wet", "testing": "fun"}
How can I select the values in ht whose key matches arr?
ht_new = ht.select {|hashes| arr.include? hashes}
ht_new # => "{"test": "abc", "testing": "fun"}"
Additionally, how can we return values from:
arr = ["abc", "123"]
ht = [{"key": "abc", "value": "test"}, {"key": "123", "value": "money"}, {"key": "doremi", "value": "rain"}}]
output # => [{"key": "abc", "value": "test"}, {"key": "123", "value": "money"}]
Only a slight change is needed:
ht.select { |k,_| arr.include? k.to_s }
##=> {:test=>"abc", :testing=>"fun"}
See Hash#select.
The block variable _ (a valid local variable), which is the value of key k, signifies to the reader that it is not used in the block calculation. Some prefer writing that |k,_v|, or some-such.
One option is mapping (Enumerable#map) the keys in arr:
arr.map.with_object({}) { |k, h| h[k] = ht[k.to_sym] }
#=> {"test"=>"abc", "testing"=>"fun", "test123"=>nil}
If you want to get rid of pairs with nil value:
arr.map.with_object({}) { |k, h| h[k] = ht[k.to_sym] if ht[k.to_sym] }
#=> {"test"=>"abc", "testing"=>"fun"}
This is an option for the last request:
ht.select{ |h| h if h.values.any? { |v| arr.include? v} }
# or
arr.map { |e| ht.find { |h| h.values.any?{ |v| v == e } } }
#=> [{:key=>"abc", :value=>"test"}, {:key=>"123", :value=>"money"}]
A straightforward way is:
ht.slice(*arr.map(&:to_sym))
# => {:test => "abc", :testing => "fun"}

Shuffling an array with a condition

Here's an array:
scramble = [
"R", "U", "R' ", "U' ", "L", "L' ", "L2", "B", "B' ",
"B2", "F", "F' ", "F2", "R2", "U2", "D", "D' ", "D2"
]
I want to shuffle it with a condition such that "R", "R' ", and "R2" are not together while shuffled, and similar for other letters.
scramble.shuffle does shuffle it, but how can I set such conditions?
Lets derive a generic solution.
Given a 2D array of groups of elements, return an array of shuffled elements such that no two consecutive elements belong to same group.
For example:
all_groups = [
["R", "R'", "R2" ],
["L", "L'", "L2" ],
["U", "U'", "U2" ],
["D", "D'", "D2" ],
["F", "F'", "F2" ],
["B", "B'", "B2" ]
]
Expected output:
["F'", "U'", "R'", "L2", "R2", "D2", "F2", "R", "L", "B", "F", "L'", "D'", "B'", "U2", "B2", "U", "D"]
TL;DR Code:
all_groups = [
["R", "R'", "R2" ],
["L", "L'", "L2" ],
["U", "U'", "U2" ],
["D", "D'", "D2" ],
["F", "F'", "F2" ],
["B", "B'", "B2" ]
]
ans = []
total = all_groups.map(&:length).reduce(:+)
# Initial shuffling
all_groups = all_groups.each { |i| i.shuffle! }.shuffle
until all_groups.empty?
# Select and pop last group
last_group = all_groups.pop
# Insert last element to our ans, and remove from group
ans.push(last_group.pop)
total -= 1
# Shuffle remaining groups
all_groups.shuffle!
# Check if any group has reached critical state
length_of_longest_group = all_groups.reduce(0) { |len, i| [len, i.length].max }
if length_of_longest_group * 2 == total + 1
# Move that group to last
# This ensures that next element picked is from this group
longest_group = all_groups.detect { |i| i.length == length_of_longest_group }
all_groups.delete(longest_group)
all_groups.push(longest_group)
end
# Insert the last group at first place. This ensures that next element
# is not from this group.
all_groups.unshift(last_group) unless last_group.empty?
end
puts ans.inspect
Examples:
all_groups = [
["R", "R'", "R2" ],
["L", "L'", "L2" ],
["U", "U'", "U2" ],
["D", "D'", "D2" ],
["F", "F'", "F2" ],
["B", "B'", "B2" ]
]
# Output 1:
ans = ["B'", "U'", "L'", "U2", "R", "B2", "F2", "R2", "D2", "L2", "D", "R'", "U", "F'", "D'", "L", "F", "B"]
# Output 2:
ans = ["U'", "R", "D", "R'", "U", "D2", "B2", "D'", "L", "B", "L2", "B'", "U2", "F'", "L'", "F", "R2", "F2"]
# Output 3:
ans = ["B", "D", "R'", "D'", "B'", "R", "F2", "L", "D2", "B2", "F'", "R2", "U'", "F", "L'", "U2", "L2", "U"]
# Output 4:
ans = ["U'", "F'", "R2", "B2", "D", "L2", "B'", "U", "R", "B", "R'", "L'", "D'", "U2", "F", "D2", "F2", "L"]
# Output 5:
ans = ["U2", "F2", "L'", "F'", "R'", "F", "D'", "B2", "D2", "L", "B", "D", "L2", "B'", "R", "U", "R2", "U'"]
all_groups = [
['a', 'aa', 'aaa', 'A', 'AA', 'AAA'],
['b', 'bb', 'bbb', 'B', 'BB', 'BBB'],
['c', 'cc', 'ccc', 'C', 'CC', 'CCC']
]
ans = ["c", "AAA", "B", "ccc", "bbb", "C", "AA", "CC", "aa", "BB", "CCC", "bb", "cc", "A", "BBB", "a", "b", "aaa"]
all_groups = [
['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8'],
['b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8']
]
# Output1:
ans = ["r2", "b7", "r1", "b5", "r7", "b6", "r3", "b8", "r4", "b3", "r5", "b1", "r6", "b4", "r8", "b2"]
# Output2:
ans = ["b6", "r8", "b2", "r1", "b4", "r2", "b8", "r7", "b3", "r4", "b5", "r5", "b7", "r3", "b1", "r6"]
Explanation:
To begin with, lets shuffle the input data. First we shuffle each group, then we shuffle the outer array.
all_groups = all_groups.each { |i| i.shuffle! }.shuffle
Now our array looks like this:
[["B", "B2", "B'"],
["F'", "F", "F2"],
["L", "L2", "L'"],
["R2", "R'", "R"],
["D", "D'", "D2"],
["U'", "U", "U2"]]
Now, if we traverse this array in column-major order, we get a prety decent shuffling of elements wherein no two consecutive elements belong to same group.
["B", "F'", "L", "R2", "D", "U'", "B2", "F", "L2", "R'", "D'", "U", "B'", "F2", "L'", "R", "D2", "U2"]
But only flaw in this is that all elements of a particular group are equidistant. Lets improve.
So we have a set of groups. Lets select any one of group and then select the last element from that group and add this element to our answer array. Now shuffle the 2D array and repeat until all elements are selected.
Also, we don't want any two consecutive elements to belong to same group, so we need to ensure that the next selection of element is from some other group. So how about this strategy:
Always pick the last group from our 2d array, and then once we shuffle we will ensure that this group is not the last group in 2d array.
In terms of psuedocode:
1. Select last group from 2d array.
2. Remove an element from this group.
3. Shuffle the order of other groups in 2d array
4. Insert the selected group at begining.
For example, lets start with this 2d array:
[["D", "D'", "D2"],
["U", "U'", "U2"],
["B2", "B", "B'"],
["F2", "F", "F'"],
["L'", "L", "L2"],
["R'", "R2", "R"]]
last group: ["R'", "R2", "R"]
Element removed: ("R")
Shuffle order of remaining groups of 2d array:
[["L'", "L", "L2"],
["B2", "B", "B'"],
["D", "D'", "D2"],
["U", "U'", "U2"],
["F2", "F", "F'"]]
2D array after inserting the popped group(the group from which we extracted an element)
[["R'", "R2"],
["L'", "L", "L2"],
["B2", "B", "B'"],
["D", "D'", "D2"],
["U", "U'", "U2"],
["F2", "F", "F'"]]
Now, since our logic selects the last group and then inserts it in the begining, we are insured that two elements of same group will never be picked in two consecutive turns.
A catch: There can be a possibility that there is a group which never reaches the last position, hence this group will never shrink, and its element will be forced to be picked consecutively once the number of elements is too less.
For example observe the output of 4 iterations of above algorithm on below input:
[['a', 'aa', 'aaa', 'A', 'AA', 'AAA'],
['b', 'bb', 'bbb', 'B', 'BB', 'BBB'],
['c', 'cc', 'ccc', 'C', 'CC', 'CCC']]
# Output 1: in this 'a' & 'aaa' are together
["CC", "bb", "C", "BB", "aa", "bbb", "AA", "CCC", "b", "c", "B", "cc", "A", "BBB", "AAA", "ccc", "a", "aaa"]
# Output 2: in this 'b' & 'BB' are together
["cc", "A", "B", "c", "AAA", "C", "a", "ccc", "bbb", "CCC", "aaa", "bb", "CC", "aa", "BBB", "AA", "b", "BB"]
# Output 3: this is perfect
["CCC", "BB", "a", "c", "BBB", "aa", "bbb", "A", "bb", "ccc", "B", "CC", "AAA", "b", "AA", "cc", "aaa", "C"]
# Output 4: in this 'c' & 'cc' are together
["CC", "bb", "AA", "b", "aa", "BBB", "aaa", "bbb", "CCC", "B", "A", "BB", "C", "a", "ccc", "AAA", "c", "cc"]
So, there are chances that when ratio of number of elements in a group to total no of elements increases, two elements of same group can be clubbed together. Hmm, lets improve further:
Since the group with maximum number of elements has most probability of having two elements combined consecutively, lets study such groups. i.e. group with maximum number of elements.
Lets say, there is a group with X number of elements in it. So what is the minimum number of other type of elements required that none of the X elements are consecutive? Simple right: insert a different element between each pair of element of this group.
x * x * x * x * x * x
So we realise, that if group has X elements it requires atleast (X-1) other type of elements so that no two elements of this group get consecutive.
Mathematically the condition can be represented as:
number_of_elements_in_longest_group == number_of_other_type_of_elements + 1
=> number_of_elements_in_longest_group == (total_number_of_elements - number_of_elements_in_longest_group) + 1
=> number_of_elements_in_longest_group * 2 == total_number_of_elements + 1
Going back to our algorithm, lets add a condition, that at any point of time, if number of remaining items is 1 less than the number of elements in largest group then we have to ensure that next element picked is from this largest group
Juksefantomet sent me this solution in discord, since he can't post here due to an account lock.
The below codeblock contains an alternative approach on how to tackle the problem at hand. This contains a fragmented solution to understand the steps on how you would normally approach a complex problem like the one presented above.
going through the various steps you can see how each condition has to be known in advance and specified to the point where your final array is not "illegal".
#illegal_order = ['1','2','3','4','5','6','7','8','9']
puts #illegal_order.count
puts "#{#illegal_order}"
#foo = []
# traverse the original array and append a random value from that into foo array
# this can of course and should be done in a loop where you check for duplicates
# this is done below in this example, fragmented to see the individual action
(0..#illegal_order.count).each do |add_value|
#temp = #illegal_order[rand(#illegal_order.count)]
unless #foo.count == #illegal_order.count
#foo.push(#temp)
end
end
# making sure it contains everything in the original array
#foo.each do |bar|
unless #illegal_order.include?(bar)
#approve = false
puts "Errored generation!"
end
end
# defining patterns, this can of course be extracted by the original array and be done alot cleaner
# but printing it out to better break it down
#pattern1 = [#illegal_order[0],#illegal_order[1],#illegal_order[2]]
#pattern2 = [#illegal_order[3],#illegal_order[4],#illegal_order[5]]
#pattern3 = [#illegal_order[6],#illegal_order[7],#illegal_order[8]]
# Let us step through the new array and use case to check for various conditions that would flag the new array as invalid
#foo.each do |step|
# setting a temp pattern based on current state
#temp_pattern = [#foo[step.to_i-1],#illegal_order[step.to_i],#illegal_order[step.to_i+1]]
case step
when #temp_pattern == #pattern1
#illegalpatterns = true
when #temp_pattern == #pattern2
#illegalpatterns = true
when #temp_pattern == #pattern3
#illegalpatterns = true
end
# checking the foo array if it contains duplicates, if yes, set conditional to true
#illegal_order.each do |count|
if #foo.count(count) > 1
#duplicates = true
end
end
end
# printing out feedback based on duplicates or not, this is where you rescramble the array if duplicate found
(#duplicates == true) ? (puts "dupes found") : (puts "looks clear. no duplicates")
# printing out feedback based on illegal patterns or not, this is where you rescramble the array if duplicate found
(#illegalpatterns == true) ? (puts "illegal patterns found") : (puts "looks clear, no illegal patterns")
puts "#{#foo}"

How do I find consecutive strings in my array that match a regex?

I'm using Ruby 2.4. I want to find consecutive tokens in my array of strings that match a regular expression. So if my regex is
/\p{L}/
and my array is
["2917", "m", "neatty", "fff", "46", "u", "28", "56"]
I would want the result to be
["m", "neatty", "fff"]
However, my attempt to do this has failed (notice the "neatty" token is repeated) ...
2.4.0 :020 > arr = ["2917", "m", "neatty", "fff", "46", "u", "28", "56"]
=> ["2917", "m", "neatty", "fff", "46", "u", "28", "56"]
2.4.0 :021 > arr.each_cons(2).select{|pair| pair.all?{|elem| elem =~ /\p{L}/ }}.flatten
=> ["m", "neatty", "neatty", "fff"]
How do I find consecutive tokens in an array that match a pattern that also don't repeat?
If r is your regex then use chunk_while
arr.chunk_while { |a,b| a[r] && b[r] }.select { |arr| arr.size > 1 }
#=> [["m", "neatty", "fff"]]
You can also use slice_when to find borders of the sub array that bound the condition:
> arr.slice_when {|x,y| !x[reg] || !y[reg] }.select {|e| e.length>1}
=> [["m", "neatty", "fff"]]
arr = ["2917", "m", "neatty", "fff", "46", "u", "28", "56", "hi", "%ya!"]
r = /\p{L}/
arr.each_with_object([[]]) { |s,a| s.match?(r) ? (a.last << s) : a << [] }.
reject { |a| a.size < 2 }
#=> [["m", "neatty", "fff"], ["hi", "%ya!"]]

How to join multiple arrays in ruby which has equal size

I have the following code:
prices = [["12.34", "23.12"], ["12.22", "66.11", "33.66", "11.11"], ["32.12"]]
names = [["dog", "pog"], ["foo", "bar", "baz", "zed"], ["qwerty"]]
numbers = [["2", "3"], ["12", "33", "444", "3023"], ["33393"]]
pics = [["url", "url"], ["url", "url", "url", "url"], ["url"]]
chars = ["A", "B", "C"]
How to join arrays, that the result array will be:
[["12.34", "dog", "2", "url", "A"], ["23.12", "pog", "3", "url", "A"],
["12.22", "foo", "12", "url", "B"], ["66.11", "bar", "33", "url", "B"],
...
["32.12", "qwerty", "33393", "url", "C"]]
I tryed join arrays through zip method, but result was:
[[["12.34", "23.12"], ["dog", "pog"], ["2", "3"], ["url", "url"], ["A", "B", "C"]]]
...
Something like this should do the trick:
chars.map.with_index {|char, i|
prices[i].zip(names[i], numbers[i], pics[i], [char].cycle)
}.flatten(1)
arr = [
[["12.34", "23.12"], ["12.22", "66.11", "33.66", "11.11"], ["32.12"]],
[["dog", "pog"], ["foo", "bar", "baz", "zed"], ["qwerty"]],
[["2", "3"], ["12", "33", "444", "3023"], ["33393"]],
[["url", "url"], ["url", "url", "url", "url"], ["url"]],
["A", "B", "C"]
] # == [prices,names,numbers,pics,chars]
arr.transpose.map do |e|
e[0...-1].reduce(:zip).map { |r| r.flatten << e.last }
end
e = chars.cycle
#=> #<Enumerator: ["A", "B", "C"]:cycle>
[prices, names, numbers, pics].map(&:flatten).transpose.map { |arr| arr << e.next }
#=> [["12.34", "dog", "2", "url", "A"],
# ["23.12", "pog", "3", "url", "B"],
# ["12.22", "foo", "12", "url", "C"],
# ["66.11", "bar", "33", "url", "A"],
# ["33.66", "baz", "444", "url", "B"],
# ["11.11", "zed", "3023", "url", "C"],
# ["32.12", "qwerty", "33393", "url", "A"]]
The steps:
a = [prices, names, numbers, pics].map(&:flatten)
#=> [["12.34", "23.12",..."32.12"],
# ["dog", "pog",..."qwerty"],
# ["2", "3",..."33393"],
# ["url",...,"url"]]
b = a.transpose
#=> [["12.34", "dog", "2", "url"],
# ["23.12", "pog", "3", "url"],
# ...
# ["32.12", "qwerty", "33393", "url"]]
b.map { |arr| arr << e.next }
#=> the above result

Resources