How to "or" all elements in the range of an array - arrays

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.

Related

Why does the str.next! in an Enumerable#map fill the array with same elements?

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",
...

Insert into a nested array

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

How to generate random string form array of elements by compared with another array of elements in swift?

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.

Swift live text field checking for characters from multiple arrays [duplicate]

This question already has answers here:
Check password string strength criteria in Swift
(2 answers)
Closed 6 years ago.
I have a text field which will serve as a password input. I have created 4 arrays
upperCaseAll = ["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"]
lowerCaseAll = ["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"]
numberAll = [1 ,2, 3, 4, 5, 6, 7, 8, 9, 0]
specialCharAll = ["-", "/", ":", ";", "(", ")", "$", "&", "#", "\"", ".", ",", "?", "!", "'", "[", "]", "{", "}", "#", "%", "^", "\\", "|", "~", "<", ">", "€", "£", "¥", "•", ".", ","]
I am trying to make the user type at least one of each type of character.
I have 4 text views that will turn from red to green once each rule is met.
Then when all rules are met the red text views turn green and the login button is enabled
My question is how do I live check the text field to see if it has satisfied a rule? I imagine it would have to check each of the arrays overtime a character is entered
Oh also should the numbers be Ints or Strings?
Thanks
It's easier if you use Set instead of Array. Here's an example:
private let upperCaseAll = Set("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
private let lowerCaseAll = Set("abcdefghijklmnopqrstuvwxyz".characters)
private let numberAll = Set("0123456789".characters)
private let specialCharAll = Set("-/:;()$&#\".,?!'[]{}#%^\\|~<>€£¥".characters)
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Determines what the new value of the text field will be
let newText = range.length == 0 ? textField.text! + string : (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
// Turn that string into a Set of Characters
let characters = Set(newText.characters)
let hasUpperCase = !characters.intersect(self.upperCaseAll).isEmpty
let hasLowerCase = !characters.intersect(self.lowerCaseAll).isEmpty
let hasNumber = !characters.intersect(self.numberAll).isEmpty
let hasSpecialChar = !characters.intersect(self.specialCharAll).isEmpty
// Now turn your 4 other views to green/red as needed
print("hasUpperCase = \(hasUpperCase), hasLowercase = \(hasLowerCase), hasNumber = \(hasNumber), hasSpecialChar = \(hasSpecialChar)")
...
return true
}
You can do it using the UITextFieldDelegate
See Apple Docs - textField(_:shouldChangeCharactersInRange:replacementString:)
The text field calls this method whenever user actions cause its text
to change. Use this method to validate text as it is typed by the
user. For example, you could use this method to prevent the user from
entering anything but numerical values.
But you should think about using a regex to check the password string. That would be more convenient.

Convert 2d array into 1d (Ruby)

The code below currently pushes a copy of #startyear into #new. I need to convert this into 1 single array, any ideas?
Forums didn't have much
startyear = [["a", "b", "z"], ["c", "d"], ["e", "f"], ["g", "h", "i", "j"]]
new = []
startyear.each do |n| #.transpose here?
puts "looping #{n}"
new.push(n)
#n.join is needed somewhere
puts "#{startyear.length} is the length of startyear"
break if startyear.length == startyear.length[4]
end
puts "your new array is : #{new}"
You can use Array#flatten:
startyear = [["a", "b", "z"], ["c", "d"], ["e", "f"], ["g", "h", "i", "j"]]
flattened = startyear.flatten
# flattened is now ["a", "b", "z", "c", "d", "e", "f", "g", "h", "i", "j"]
Array#flatten is the obvious method to use here, but as is generally the case with Ruby, there are alternatives. Here are two.
Use Enumerable#flat_map and Object#itself
startyear.flat_map(&:itself)
#=> ["a", "b", "z", "c", "d", "e", "f", "g", "h", "i", "j"]
itself was introduced in Ruby v2.2. For earlier versions, use:
startyear.flat_map { |a| a }
Use Enumerable#reduce (aka inject)
startyear.reduce(:+)
#=> ["a", "b", "z", "c", "d", "e", "f", "g", "h", "i", "j"]

Resources