I have cars described in a file in this form:
CAR
Audi
1x1
XXX
XXXXX
_X
_X
I want to insert the cars from this file into a 10 x 10 nested array.
The second line are coordinates (R x C) from the left top point for the start of the car. Third to fifth lines show the shape of the car.
OUTPUT= [["O","O","O","O","O","O","O","O","O","O"],
["O","X","X","X","O","O","O","O","O","O"],
["O","X","X","X","X","X","O","O","O","O"],
["O","O","X","O","O","O","O","O","O","O"],
["O","O","X","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
INPUT arr =[]
This should work, pretty tricky. Sure there is a better way.
First map the file into a hash.
car_array = []
File.open('car_array.txt').each_with_index do |line, idx|
car_array << line.chomp
end
p car_array
# => ["CAR", "Audi", "1x1", "XXX", "XXXXX", "_X", "_X", "CAR", "BMW", "5x1", "__X", "XXX", "X", "XX"]
car_hash = {}
key = nil
car_array.each_with_index do |e, index|
key = car_array[index-1].downcase.to_sym if car_array[index-2] == "CAR"
car_hash[key] = [e] if car_array[index-2] == "CAR"
car_hash[key] << e if car_hash[key] unless (car_array[index-2] == "CAR" || car_array[index-1] == "CAR" || car_array[index] == "CAR")
end
p car_hash
# => {:audi=>["1x1", "XXX", "XXXXX", "_X", "_X"], :bmw=>["5x1", "__X", "XXX", "X", "XX"]}
car_hash.transform_values do |array|
h = {start: [], points: []}
h[:start] = array[0].split('x').map(&:to_i).map!{ |e| e}
(array.size - 1).times do |n|
h[:points] << array[n+1]
end
array[0] = h
(array.size - 1).times do |n|
array.pop
end
end
car_hash.transform_values! {|v| v[0]}
p car_hash
# => {:audi=>{:start=>[1, 1], :points=>["XXX", "XXXXX", "_X", "_X"]}, :bmw=>{:start=>[5, 1], :points=>["__X", "XXX", "X", "XX"]}}
car_hash.each_value do |car|
car[:points].map! do |point|
point.scan(/./)
end
row = car[:start][0] - 1
col = car[:start][1] - 1
car[:points].map! do |points|
delta_col = 0
row += 1
points.map! do |point|
delta_col +=1
point == 'X' ? [row, col + delta_col] : nil
end
points.compact
end
car[:points] = car[:points].flatten(1)
end
p car_hash
# => {:audi=>{:start=>[1, 1], :points=>[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [3, 2], [4, 2]]}, :bmw=>{:start=>[5, 1], :points=>[[5, 3], [6, 1], [6, 2], [6, 3], [7, 1], [8, 1], [8, 2]]}}
Then us the hash to build the table.
table_model = Array.new(10) {Array.new(10,'O')}
def print_table (table)
table.each {|row| p row}
end
audi_table = table_model.dup
car_hash[:audi][:points].each { |point| audi_table[point[0]][point[1]] = 'x' }
print_table audi_table
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "x", "x", "x", "O", "O", "O", "O", "O", "O"]
# => ["O", "x", "x", "x", "x", "x", "O", "O", "O", "O"]
# => ["O", "O", "x", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "x", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
Related
Enumerable#map creates an array with the return values in the block after it's yielded.
In such case, say:
v = 'a'
26.times.map { |i| v.ord.+(i).chr }
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
But why does the following codes fills the array with same elements?
v = '`'
26.times.map { v.next! }
# => ["z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z"]
v = '`'
Array.new(26) { v.next! }
# => ["z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z"]
Shouldn't they all have elements a to z?
Again, this works:
v = '`'
Array.new(26) { v = v.succ }
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
Actually I am trying to do:
v = "\xf0\x9d\x93\xa9"
('a'..'z').reduce({}) { |h, i| h.merge(i.intern => v = v.succ) }
# => {:a=>"𝓪", :b=>"𝓫", :c=>"𝓬", :d=>"𝓭", :e=>"𝓮", :f=>"𝓯", :g=>"𝓰", :h=>"𝓱", :i=>"𝓲", :j=>"𝓳", :k=>"𝓴", :l=>"𝓵", :m=>"𝓶", :n=>"𝓷", :o=>"𝓸", :p=>"𝓹", :q=>"𝓺", :r=>"𝓻", :s=>"𝓼", :t=>"𝓽", :u=>"𝓾", :v=>"𝓿", :w=>"𝔀", :x=>"𝔁", :y=>"𝔂", :z=>"𝔃"}
But I get all z's instead while using succ! / next!
v = "\xf0\x9d\x93\xa9"
('a'..'z').reduce({}) { |h, i| h.merge(i.intern => v.succ!) }
# => {:a=>"𝔃", :b=>"𝔃", :c=>"𝔃", :d=>"𝔃", :e=>"𝔃", :f=>"𝔃", :g=>"𝔃", :h=>"𝔃", :i=>"𝔃", :j=>"𝔃", :k=>"𝔃", :l=>"𝔃", :m=>"𝔃", :n=>"𝔃", :o=>"𝔃", :p=>"𝔃", :q=>"𝔃", :r=>"𝔃", :s=>"𝔃", :t=>"𝔃", :u=>"𝔃", :v=>"𝔃", :w=>"𝔃", :x=>"𝔃", :y=>"𝔃", :z=>"𝔃"}
Except succ! / next! doesn't change the memory location and object_id, aren't v.succ! and v = v.succ same?
When you call next! or succ! on a variable str, object assigned to this variable is mutated and a reference to this object is returned. If str = 'a' and you call str.next! 26 times, str becomes z. Every time next! is called, a reference to the same object is returned. As a result, you get an array of 26 references to the same object. That's why all of the elements in the array are the same.
You can test that by checking object_id of array elements:
pry(main)> str = 'a'
'a'
pry(main)> array = 3.times.map{ str.next!}
=> ["d", "d", "d"]
pry(main)> array.map(&:object_id)
=> [47056742362940, 47056742362940, 47056742362940]
pry(main)> array.map(&:object_id).uniq
=> [47056742362940]
When you edit str, all array elements are updated:
[39] pry(main)> str << "b"
=> "db"
[40] pry(main)> array
=> ["db", "db", "db"]
[41] pry(main)> str.replace
str.replace
[41] pry(main)> str.replace('a')
=> "a"
[42] pry(main)> array
=> ["a", "a", "a"]
If you want to have an array with the whole alphabet, you need to copy the string after changing current letter, see below:
[25] pry(main)> str = 'a'
=> "a"
[26] pry(main)> 25.times.map{ str.next!.dup}
=> ["b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"w",
"x",
"y",
"z"]
You can also use a range:
[32] pry(main)> ('a'..'z').to_a
=> ["a",
"b",
"c",
"d",
"e",
"f",
...
I aware to get random character from a string. From here is the code,
func randomString(_ length: Int) -> String {
let master = Array("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_123456789".characters) //0...62 = 63
var randomString = ""
for _ in 1...length{
let random = arc4random_uniform(UInt32(master.count))
randomString.append(String(master[Int(random)]))
}
return randomString
}
Usage :
var randomArray1 = [String]()
for _ in 0...62{
randomArray1.append(self.randomString(1))
}
Here, If randomArray1.append(self.randomString(x)), then x = 1...N
Checking repeated elements :
var sameElementCatcher = [String]()
for x in 0...62{
let element = randomArray1[x]
randomArray1[x] = ""
if randomArray1.contains(element){
sameElementCatcher.append(element)
}
}
print("Same Elements")
print(sameElementCatcher.count != 0 ? sameElementCatcher : "Array count is zero")
Output:
Same Elements
["_", "u", "8", "7", "E", "P", "u", "y", "C", "-", "C", "x", "l", "j",
"t", "D", "U", "2", "e", "2"]
But I need to get array of 62 unique random characters from master by compared with randomArray1. i.e., Array count is zero
How can I achieve this without delay?
Note:
Also, I read this post also I have a answer for shuffling array. But this post different from shuffling only, Please, see usage.
Did you try like this?
What I understand from your question. generate a random text where all the characters are unique.
Before appending your random string to your array check is array have that char then append into your array.
func randomString(_ length: Int) -> String {
let master = Array("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_123456789".characters) //0...62 = 63
var randomString = ""
for _ in 1...length{
let random = arc4random_uniform(UInt32(master.count))
randomString.append(String(master[Int(random)]))
}
return randomString
}
var randomArray1 = [String]()
var tempRandomString = ""
let totalRandomCount = 62
var randomArrayCount = 0
while (totalRandomCount > randomArrayCount) {
tempRandomString = randomString(1)
if !randomArray1.contains(tempRandomString) {
randomArray1.append(tempRandomString)
randomArrayCount+=1
}
}
print(randomArray1)
Output: ["X", "u", "j", "1", "n", "E", "D", "q", "U", "6", "T", "O", "f", "J", "i", "c", "W", "V", "G", "R", "k", "7", "_", "8", "-", "l", "w", "4", "e", "Q", "C", "m", "M", "Y", "o", "S", "B", "2", "Z", "P", "p", "N", "y", "H", "a", "h", "z", "s", "b", "A", "3", "g", "x", "L", "v", "F", "d", "r", "t", "K", "9", "5"]
I tried this with playground. For this output 199 times loop executed.
If anyone knows better than this update yours.
How to combine 2 arrays like this
a = ["x","y","z"]
b = [["a","b"],["c","d"],["e","f"]]
expected output:
[["a","b","x" ],["c","d","y"],["e","f","z"]]
Is there any inbuilt method?
There is. You can use Array#zip in conjunction with Array#flatten:
b.zip(a).map(&:flatten)
#=> [["a", "b", "x"], ["c", "d", "y"], ["e", "f", "z"]]
another way is:
[b, a].transpose.map(&:flatten)
#=> [["a", "b", "x"], ["c", "d", "y"], ["e", "f", "z"]]
:)
Here is one more way to do this:
a = ["x","y","z"]
b = [["a","b"],["c","d"],["e","f"]]
b.map.with_index {|arr, idx| arr << a[idx]}
#=> [["a", "b", "x"], ["c", "d", "y"], ["e", "f", "z"]]
enum = a.to_enum
b.map { |arr| arr << enum.next }
#=> [["a", "b", "x"], ["c", "d", "y"], ["e", "f", "z"]]
I have:
stuff = [1, 2, "a", "b", "c", "d", 4, 5, "z", "l", "m", "l", 5, 4, 4, 77]
Numbers come in groups of multiples of two, and letters come in groups of multiples of four.
I want to group numbers in twos and letters in fours like this:
stuff_processed = [
[1, 2],
["a", "b", "c", "d"],
[4, 5],
["z", "l", "m", "l"],
[5, 4],
[4, 77]
]
The order inside of an array that holds numbers or letters is important, the order between the arrays I do not care about.
I know stuff.each_slice(2).to_a will take me part of the way. I can't figure out how to get all the way to what I need though.
stuff
.chunk(&:class)
.flat_map{|klass, a| a.each_slice(klass == Fixnum ? 2 : 4).to_a}
# => [[1, 2], ["a", "b", "c", "d"], [4, 5], ["z", "l", "m", "l"], [5, 4], [4, 77]]
arr = [1, 2, "a", "b", "c", "d", 4, 5, "z", "l", "m", "l", "s", "t",
"u", "v", 5, 4, 4, 77, 91, 65]
H = { Fixnum=>1, String=>3 }
count = 0
arr.slice_when do |a,b|
if a.class == b.class && count < H[a.class]
count += 1
false
else
count = 0
true
end
end.to_a
# => [[1, 2], ["a", "b", "c", "d"], [4, 5], ["z", "l", "m", "l"],
# ["s", "t", "u", "v"], [5, 4], [4, 77], [91, 65]]
See Enumerable#slice_when, which first appeared in Ruby v2.2.
This Array#conditional_slice method accepts a Block and returns an Enumerator :
stuff = [1, 2, "a", "b", "c", "d", 4, 5, "z", "l", "m", "l", 5, 4, 4, 77]
class Array
def conditional_slice(&block)
clone = self.dup
Enumerator.new do |yielder|
until clone.empty? do
yielder << clone.shift(block_given? ? block.call(clone.first) : 1)
end
end
end
end
sliced_stuff = stuff.conditional_slice{|x| x.is_a?(Numeric) ? 2 : 4}
puts sliced_stuff.to_a.inspect
# => [[1, 2], ["a", "b", "c", "d"], [4, 5], ["z", "l", "m", "l"], [5, 4], [4, 77]]
I am trying to say if x == consonants[0] or [1] or [2] all the way to [21] on one line. For some reason I thought consonants[0..21] would work but it doesn't:
consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t",
"v", "w", "x", "y", "z"]
new_first = ["m", "a", "t", "t", "h", "e", "w", "s"]
new_first.each do |x|
if x == consonants[0]
puts x.next!
elsif x == consonants[1]
puts x.next!
elsif x == consonants[2]
puts x.next!
elsif x == consonants[3]
puts x.next!
else
puts "test failed"
end
end
There's several ways to crack this nut, but it depends on your performance concerns, and how extensible this needs to be. Normally a chain of if statements which are of the form x == y and x == z can be folded into:
case (x)
when y, z
# ... Executed on a match
end
In your case you can even do this by using your array as a list of valid values:
case (x)
when *constants
puts x.next!
end
For larger lists you might want to fold this up into a Set since these are optimized for include? tests:
consonants_set = Set.new(consonants)
if (consonants_set.include?(x))
puts x.next!
end
Since you're doing single letter matches you have a lot more options. For example, a regular expression:
consonants_regexp = Regexp.new('[%s]' % consonants.join)
if (consonants_regexp.match(x))
puts x.next!
end
Or you can even do a simple substring match:
consonants_string = consonants.join
if (consonants_string[x])
puts x.next!
end
Worth noting but you can iterate over the characters in strings:
'cwzbrly'.each_char do |c|
puts c
end
That avoids the need to create and/or type in long arrays of the form [ 'a', 'b', ... ].
You can do this:
if consonants.include? (x)
your-code-here
end
This will check if there's an element equal to x inside the array.
You can use regular expression
if x =~ /[aeiou]/
puts 'test failed'
else
puts x.next!
end
/[aeiou]/ is saying match anything that is a, e, i, o, or u.
This will eliminate the need to create an array of consonants.
If I correctly understand and you're expecting nuuixt as a result, you can do:
new_first.select { |letter| consonants.include?(letter) && letter.next! }
&& works here this way: if consonants.include?(letter) evaluates to true then block return letter.next! .
Consider this:
consonants = ('a' .. 'z').to_a - %w[a e i o u] # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
shifted_consonants = consonants.zip(consonants.rotate).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
'matthews'.chars.map{ |c| shifted_consonants[c] || c } # => ["n", "a", "v", "v", "j", "e", "x", "t"]
That took the range of 'a' to 'z' and converted it to an array, then subtracted the array of vowels, resulting in only consonants.
Next, it turned the array of consonants into a hash/look-up table shifted_consonants where each key is a current consonant and the value is the next consonant.
Finally, it takes each character in 'matthews' and looks to see if there is a value in shifted_consonants for that character. If not, nil is returned, which triggers || and returns the current character. If there is a hit in the hash, the next value for that consonant is returned, which short-circuits the || "or".
An alternate would be to take advantage of tr:
consonants = (('a' .. 'z').to_a - %w[a e i o u]).join # => "bcdfghjklmnpqrstvwxyz"
shifted_consonants = consonants.chars.rotate.join # => "cdfghjklmnpqrstvwxyzb"
'matthews'.tr(consonants, shifted_consonants).chars # => ["n", "a", "v", "v", "j", "e", "x", "t"]
Checking for speed:
CONSONANTS = ('a' .. 'z').to_a - %w[a e i o u] # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
SHIFTED_CONSONANTS_HASH = CONSONANTS.zip(CONSONANTS.rotate).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
CONSONANTS_STR = (('a' .. 'z').to_a - %w[a e i o u]).join # => "bcdfghjklmnpqrstvwxyz"
SHIFTED_CONSONANTS_STR = CONSONANTS_STR.chars.rotate.join # => "cdfghjklmnpqrstvwxyzb"
require 'fruity'
sample_string = 'matthews'
compare do
use_hash { sample_string.chars.map{ |c| SHIFTED_CONSONANTS_HASH[c] || c } }
use_tr { sample_string.tr(CONSONANTS_STR, SHIFTED_CONSONANTS_STR).chars }
end
# >> Running each test 2048 times. Test will take about 1 second.
# >> use_tr is faster than use_hash by 10.000000000000009% ± 10.0%
The longer the sample string is, the greater the difference. Changing to:
sample_string = 'matthews' * 1000
I see a result of:
# >> Running each test 4 times. Test will take about 1 second.
# >> use_tr is faster than use_hash by 4x ± 0.1
Found in a comment, NOT in the question where it belongs...
my goal is to change the constonant to the next consonant and the vowel to the next vowel for the arraynew_first = ["m", "a",
Adjusting for that here's some changes. You can unravel the deltas:
ALPHABET = ('a' .. 'z').to_a
VOWELS = %w[a e i o u]
CONSONANTS = ALPHABET - VOWELS # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
SHIFTED_CONSONANTS = CONSONANTS.rotate # => ["c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z", "b"]
SHIFTED_VOWELS = VOWELS.rotate # => ["e", "i", "o", "u", "a"]
SHIFTED_CONSONANTS_HASH = CONSONANTS.zip(SHIFTED_CONSONANTS).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
SHIFTED_VOWELS_HASH = VOWELS.zip(SHIFTED_VOWELS).to_h # => {"a"=>"e", "e"=>"i", "i"=>"o", "o"=>"u", "u"=>"a"}
sample_string = 'matthews'
sample_string.chars.map{ |c| SHIFTED_CONSONANTS_HASH[c] || SHIFTED_VOWELS_HASH[c] } # => ["n", "e", "v", "v", "j", "i", "x", "t"]
CONSONANTS_STR = CONSONANTS.join # => "bcdfghjklmnpqrstvwxyz"
SHIFTED_CONSONANTS_STR = SHIFTED_CONSONANTS.join # => "cdfghjklmnpqrstvwxyzb"
SHIFTED_VOWELS_STR = SHIFTED_VOWELS.join # => "eioua"
CHARACTERS_STR = (CONSONANTS + VOWELS).join # => "bcdfghjklmnpqrstvwxyzaeiou"
SHIFTED_CHARACTERS_STR = SHIFTED_CONSONANTS_STR + SHIFTED_VOWELS_STR # => "cdfghjklmnpqrstvwxyzbeioua"
sample_string.tr(CHARACTERS_STR, SHIFTED_CHARACTERS_STR).chars # => ["n", "e", "v", "v", "j", "i", "x", "t"]
The changes won't affect the speed of the actual code: tr would still outrun using the hash lookups.