Multiple mapping of arrays within arrays - Ruby [closed] - ruby

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I want to go through an array in an array with arrays in those.
The array should contain 5 arrays, and in each of those arrays are five more arrays, the generation of the numbers is fine, but when I go through each of the arrays to map them with something else, I only get back an array with 5 arrays in it.
def random_map
#row = []
#col = []
#map = []
5.times do |row|
5.times do |col|
#c = rand(3)
#d = [#c]
#col << #d
end
#row << #col
#col = []
end
#map << #row
#map.map! do |row|
row.map! do |col|
col.map! do |element|
case(element[0])
when 0
element[0] = "BG"
when 1
element[0] = "B1"
when 2
element[0] = "TR"
end
end
end
end
end
Anyone know what's up with the mapping?

I'm not positive, but I think this is what you're trying to do:
def random_map
Array.new(5) do
Array.new(5) do
Array.new(5) do
%w{BG B1 TR}.sample
end
end
end
end
Running it generates an Array of 5 Arrays of 5 Arrays of 5 Strings, chosen at random form the three strings "BG", "B1", and "TR":
random_map
# => [[["B1", "BG", "BG", "B1", "B1"],
["B1", "B1", "BG", "TR", "B1"],
["B1", "B1", "TR", "TR", "TR"],
["B1", "TR", "TR", "BG", "B1"],
["BG", "TR", "BG", "TR", "TR"]],
[["B1", "TR", "TR", "BG", "TR"],
["BG", "B1", "TR", "BG", "BG"],
["B1", "B1", "BG", "TR", "BG"],
["BG", "BG", "TR", "B1", "B1"],
["B1", "BG", "BG", "BG", "BG"]],
[["B1", "BG", "BG", "B1", "TR"],
["BG", "BG", "B1", "B1", "TR"],
["B1", "BG", "B1", "TR", "TR"],
["BG", "TR", "B1", "B1", "BG"],
["TR", "TR", "BG", "TR", "B1"]],
[["BG", "B1", "BG", "BG", "BG"],
["B1", "B1", "TR", "TR", "B1"],
["BG", "B1", "TR", "B1", "TR"],
["TR", "TR", "TR", "BG", "B1"],
["TR", "B1", "BG", "TR", "BG"]],
[["B1", "B1", "BG", "BG", "TR"],
["TR", "TR", "B1", "BG", "BG"],
["TR", "BG", "B1", "BG", "TR"],
["BG", "B1", "TR", "BG", "TR"],
["B1", "TR", "BG", "B1", "B1"]]]
I'm specifically not using the instance variables #row, #col, #map, #c, or #d because it's not clear that you actually want to be setting them; it looks like you're using them as simply local variables.

Related

Reduce an array of nested hashes to a flattened array of key/value pairs

Given the following data structure:
[
[:created_at, "07/28/2017"],
[:valid_record, "true"],
[:cs_details, { gender: 'm', race: 'w', language: nil } ],
[:co_details, { description: 'possess', extra: { a: 'a', b: 'b', c: 'c'} } ]
]
I want an array of arrays of key/value pairs:
[
[:created_at, "07/28/2017"],
[:valid_record, "true"],
[:gender, 'm'],
[:race, 'w'],
[:description, "process"]
[:a, "a"],
[:b, "b"],
[:c, "c"]
]
Problem is I don't know how to flatten those hashes. flatten doesn't do anything:
arr.map(&:flatten)
=> [[:created_at, "07/28/2017"], [:valid_record, "true"], [:cs_details, {:gender=>"m", :race=>"w", :language=>nil}], [:co_details, {:description=>"possess", :extra=>{:a=>"a", :b=>"b", :c=>"c"}}]]
So I know flat_map won't help either. I cannot even turn those hashes to arrays using to_a:
arr.map(&:to_a)
=> [[:created_at, "07/28/2017"], [:valid_record, "true"], [:cs_details, {:gender=>"m", :race=>"w", :language=>nil}], [:co_details, {:description=>"possess", :extra=>{:a=>"a", :b=>"b", :c=>"c"}}]]
The problem with the above methods is that they work top level index only. And these hashes are nested in arrays. So I try reduce and then invoke flat_map on result:
arr.reduce([]) do |acc, (k,v)|
if v.is_a?(Hash)
acc << v.map(&:to_a)
else
acc << [k,v]
end
acc
end.flat_map(&:to_a)
=> [:created_at, "07/28/2017", :valid_record, "true", [:gender, "m"], [:race, "w"], [:language, nil], [:description, "possess"], [:extra, {:a=>"a", :b=>"b", :c=>"c"}]]
Not quite there, but closer. Any suggestions?
▶ flattener = ->(k, v) do
▷ case v
▷ when Enumerable then v.flat_map(&flattener)
▷ when NilClass then []
▷ else [k, v]
▷ end
▷ end
#⇒ #<Proc:0x000000032169e0#(pry):26 (lambda)>
▶ input.flat_map(&flattener).each_slice(2).to_a
#⇒ [
# [:created_at, "07/28/2017"],
# [:valid_record, "true"],
# [:gender, "m"],
# [:race, "w"],
# [:description, "possess"],
# [:a, "a"],
# [:b, "b"],
# [:c, "c"]
# ]
I think you would benefit from writing a helper function that will be called on each item in the array. So that the results are uniform, we will make sure that this function always returns an array of arrays. In other words, an array containing one or more "entries," depending on whether the thing at index 1 is a hash.
def extract_entries((k,v))
if v.is_a? Hash
v.to_a
else
[[k, v]]
end
end
Trying it out:
require 'pp'
pp data.map {|item| extract_entries(item)}
Output:
[[[:created_at, "07/28/2017"]],
[[:valid_record, "true"]],
[[:gender, "m"], [:race, "w"], [:language, nil]],
[[:description, "possess"], [:extra, {:a=>"a", :b=>"b", :c=>"c"}]]]
Now, we can flatten by one level to reach your desired format:
pp data.map {|item| extract_entries(item)}.flatten(1)
Output:
[[:created_at, "07/28/2017"],
[:valid_record, "true"],
[:gender, "m"],
[:race, "w"],
[:language, nil],
[:description, "possess"],
[:extra, {:a=>"a", :b=>"b", :c=>"c"}]]
def to_flat(arr)
arr.flat_map { |k,v| v.is_a?(Hash) ? to_flat(v.compact) : [[k, v]] }
end
test
arr = [ [:created_at, "07/28/2017"],
[:valid_record, "true"],
[:cs_details, { gender: 'm', race: 'w', language: nil } ],
[:co_details, { description: 'possess', extra: { a: 'a', b: 'b', c: 'c'} } ] ]
to_flat(arr)
#=> [[:created_at, "07/28/2017"], [:valid_record, "true"], [:gender, "m"],
# [:race, "w"], [:description, "possess"], [:a, "a"], [:b, "b"], [:c, "c"]]

Method to front capitalized words

I am trying to move capitalized words to the front of the sentence. I expect to get this:
capsort(["a", "This", "test.", "Is"])
#=> ["This", "Is", "a", "test."]
capsort(["to", "return", "I" , "something", "Want", "It", "like", "this."])
#=> ["I", "Want", "It", "to", "return", "something", "like", "this."]
The key is maintaining the word order.
I feel like I'm very close.
def capsort(words)
array_cap = []
array_lowcase = []
words.each { |x| x.start_with? ~/[A-Z]/ ? array_cap.push(x) : array_lowcase.push(x) }
words= array_cap << array_lowcase
end
Curious to see what other elegant solutions might be.
The question was changed radically, making my earlier answer completely wrong. Now, the answer is:
def capsort(strings)
strings.partition(&/\p{Upper}/.method(:match)).flatten
end
capsort(["a", "This", "test.", "Is"])
# => ["This", "Is", "a", "test."]
My earlier answer was:
def capsort(strings)
strings.sort
end
capsort(["a", "This", "test.", "Is"])
# => ["Is", "This", "a", "test."]
'Z' < 'a' # => true, there's nothing to be done.
def capsort(words)
words.partition{|s| s =~ /\A[A-Z]/}.flatten
end
capsort(["a", "This", "test.", "Is"])
# => ["This", "Is", "a", "test."]
capsort(["to", "return", "I" , "something", "Want", "It", "like", "this."])
# => ["I", "Want", "It", "to", "return", "something", "like", "this."]
def capsort(words)
caps = words.select{ |x| x =~ /^[A-Z]/ }
lows = words.select{ |x| x !~ /^[A-Z]/ }
caps.concat(lows)
end

Removing duplicates from a csv file using ruby

I have a csv file with the following data
Sno Scenario Result Description
1 Sce_1 Pass Pass
2 Sce_2 Pass Pass
1 Sce_1 Fail Failed
In this scenario I have 2 same serial numbers. I would like to see only the row which has result Pass and remove the rest of the duplicate rows.
I have tried the following but still am not able to get it!
CSV.open('New.csv', 'w') do |csv|
CSV.read('Merged_files.csv').uniq!{|x| x[1]}.each do |row|
csv << row
end
end
Can anyone help me in getting the logic!
For purposes of illustration, I've added a fourth row to your table:
require 'csv'
arr = CSV.read("x.csv")
#=> [["Sno", "Scenario", "Result", "Description"],
# ["1", "Sce_1", "Pass", "Pass"],
# ["2", "Sce_2", "Pass", "Pass"],
# ["1", "Sec_1", "Fail", "Pass"],
# ["3", "Sec_3", "Fail", "Pass"]]
You can remove the unwanted elements as follows:
arr[1..-1].group_by(&:first).map { |_,a|
(a.size > 1) ? a.reject { |e| e[2]=="Fail" } : a }
#=> [[["1", "Sce_1", "Pass", "Pass"]],
# [["2", "Sce_2", "Pass", "Pass"]],
# [["3", "Sec_3", "Fail", "Pass"]]]
The steps:
h = arr[1..-1].group_by(&:first)
#=> {"1"=>[["1", "Sce_1", "Pass", "Pass"],
# ["1", "Sec_1", "Fail", "Pass"]],
# "2"=>[["2", "Sce_2", "Pass", "Pass"]],
# "3"=>[["3", "Sec_3", "Fail", "Pass"]]}
h.map { |_,a| (a.size > 1) ? a.reject { |e| e[2]=="Fail" } : a }
#=> [[["1", "Sce_1", "Pass", "Pass"]],
# [["2", "Sce_2", "Pass", "Pass"]],
# [["3", "Sec_3", "Fail", "Pass"]]]
If, for a given Sno/Scenario there is at most one "Pass" row, you can use Enumerable#flat_map instead:
a = h.flat_map { |_,a| (a.size > 1) ? a.reject { |e| e[2]=="Fail" } : a }
#=> [["1", "Sce_1", "Pass", "Pass"],
# ["2", "Sce_2", "Pass", "Pass"],
# ["3", "Sec_3", "Fail", "Pass"]]
If you wish to add back the header row:
a.unshift(arr.first)
#=> [["Sno", "Scenario", "Result", "Description"],
# ["1", "Sce_1", "Pass", "Pass"],
# ["2", "Sce_2", "Pass", "Pass"],
# ["3", "Sec_3", "Fail", "Pass"]]
If you want to exclude all "Fail" rows, even if there is no correponding "Pass" row (as for Sno == "3"), you can do this:
h.flat_map { |_,a| a.reject { |e| e[2]=="Fail" } }
#=> [["1", "Sce_1", "Pass", "Pass"],
# ["2", "Sce_2", "Pass", "Pass"]]

Sort an array alphabetically in Ruby but with capitalised words at the end

I'm trying to sort a string alphabetically in Ruby. The problem is, it puts the capitalized words at the beginning of the string, but I want them at the end.
My original string is: ["Apple", "banana", "Zebra", "orange"]
What I want: ["banana", "orange", "Apple", "Zebra"]
Swap the case for sorting:
["Apple", "banana", "Zebra", "orange"].sort_by(&:swapcase)
#=> ["banana", "orange", "Apple", "Zebra"]
I assume you want to sort the string but move capitalized words to the end of the sorting algorithm.
This works if only the first character is capitalized:
p ["banana", "Banana","orange", "Apple", ].sort_by{|x| [x[0].upcase==x[0] ? 1:0,x] } #=> ["banana", "orange", "Apple", "Banana"]
p ["banana", "Banana","orange", "Apple", ].sort #=> ["Apple", "Banana", "banana", "orange"]
Edit: Apparently according to Cary Swoveland, this will be faster than using sort_by, and I'm inclined to believe him because sort is already so well optimized.
def new_sort(array)
cap, non = array.partition {|el|el[0] == el[0].upcase}
non.sort + cap.sort
end
puts new_sort ["Apple", "banana", "Zebra", "orange"]
# banna
# orange
# Apple
# Zebra
puts new_sort ["THREE", "two", "one",]
# one
# two
# THREE
Yet another way:
arr = %w{ Apple banana Zebra Camel iPhone waffles Pancakes }
# => ["Apple", "banana", "Zebra", "Camel", "iPhone", "waffles", "Pancakes"]
arr.sort.rotate(arr.count { |s| s[/^[A-Z]/] })
# => ["banana", "iPhone", "waffles", "Apple", "Camel", "Pancakes", "Zebra"]
We have:
a = arr.sort
# => ["Apple", "Camel", "Pancakes", "Zebra", "banana", "iPhone", "waffles"]
nbr_capitalized_words = arr.count { |s| s[/^[A-Z]/] }
#=> 4
a.rotate(nbr_capitalized_words)
#=> ["banana", "iPhone", "waffles", "Apple", "Camel", "Pancakes", "Zebra"]

Iterating through a two dimensional array in Ruby and substituting a string for every instance

win_array = [ ["TL", "TM", "TR"], ["ML", "MM", "MR"], ["BL", "BM", "BR"],
["TL", "ML", "BL"], ["TM", "MM", "BM"] , ["TR", "MR", "BR"],
["TL", "MM", "BR"], ["TR", "MM", "BL"] ]
win_array[0].map! {|x| x == "TR" ? "1" : x } #works for [0] array
win_array[0..7].map! {|x| x == "TR" ? "1" : x } #doesn't work
win_array.each.map! {|x| x == "TR" ? "1" : x } #doesn't work
win_array.each {|x| x.map! == "TR" ? "1" : x } #doesn't work
I am trying to permanently change every instance of "TR" in this 2D array to either an integer or a string. I have tried to use the .map! methods outlined above with little luck. Any help would be greatly appreciated.
I passed the block you used to change win_array[0] (in the first attempt) to a double-nested map! call, so it sweeps over each element.
win_array.map! { |a| a.map! { |x| x == "TR" ? "1" : x } }
If each row of the array has the same number of elements (as in your example), you could convert the array to a Matrix object, apply Matrix#collect (aka map), and then convert the matrix back to an array (unless, of course, it is more convenient to keep it as a Matrix object for further processing).
require 'matrix'
win_array = [ ["TL", "TM", "TR"], ["ML", "MM", "MR"], ["BL", "BM", "BR"],
["TL", "ML", "BL"], ["TM", "MM", "BM"], ["TR", "MR", "BR"],
["TL", "MM", "BR"], ["TR", "MM", "BL"] ]
win_array = Matrix[*win_array].map { |e| (e == "TR") ? "1" : e }.to_a
#=> [ ["TL", "TM", "1" ], ["ML", "MM", "MR"], ["BL", "BM", "BR"],
# ["TL", "ML", "BL"], ["TM", "MM", "BM"], ["1" , "MR", "BR"],
# ["TL", "MM", "BR"], ["1" , "MM", "BL"] ]
Ruby OOP tic-tac-toe hash / conditional / itiration
This may help you, but maybe not lol
We can't figure out how to output our tie function.

Resources