Related
I recently started learning swift via an online course.
I was given the Task to generate a passwort out of a given Array containing characters.
We learned mainly two code examples to randomly choose one.
variable[Int.random(in: 0...25)]
variable.randomElement()
Both work just fine when pulling out one single element out of an array but only "variable[Int.random(in: 0...25)" when combined multiple times with a plus (+).
Why is that?
I looked up the documentation but couldn't find an answer
https://developer.apple.com/documentation/swift/array/2994747-randomelement
Explanation:
This code works:
let alphabet = ["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"]
//The number of letters in alphabet equals 26
var password = alphabet[Int.random(in: 0...25)] + alphabet[Int.random(in: 0...25)] + alphabet[Int.random(in: 0...25)] + alphabet[Int.random(in: 0...25)] + alphabet[Int.random(in: 0...25)] + alphabet[Int.random(in: 0...25)]
print(password)
This code does not work, because "randomElement()" gets grey after combining multiple with plus (why?)
let alphabet = ["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"]
//The number of letters in alphabet equals 26
var password = alphabet.randomElement() + alphabet.randomElement() + alphabet.randomElement() + alphabet.randomElement() + alphabet.randomElement() + alphabet.randomElement()
print(password)
Edit:
Thanks for the fast explanation guys !
The difference is that randomElement returns an optional string ( String?), as opposed to the subscript that returns a non-optional. Why does randomElement return an optional string? Well, what if the array is empty?
And Swift can't figure out how to add 2 optional strings (let alone 6! So it just gives up). One way to fix this is to force-unwrap the return values of randomElement:
let password = alphabet.randomElement()! + alphabet.randomElement()! + alphabet.randomElement()! + alphabet.randomElement()! + alphabet.randomElement()! + alphabet.randomElement()!
We know the array is not empty, so we can safely force-unwrap here.
Arguably, randomElement is the better method to use here, because it forces you to think about the situation where the array is empty, and handle it accordingly. The first approach with subscripts doesn't have checks for whether the array is empty, or whether the indices are correct, etc.
This is because of the different return type between the two approaches.
Indexing an array of strings return, as you would expect, the String at that index.
However the .randomElement() function has a different signature: it return an optional element of the sequence, in your case an optional string (String?).
The '+' operator is defined for strings, but not for optional Strings. To add together the elements returned from .randomElement() you would need to unwrap them first. Given you have a closed sequence you it would be safe to force unwrap them with '!':
var password = alphabet.randomElement()! + alphabet.randomElement()! + alphabet.randomElement()! + alphabet.randomElement()! + alphabet.randomElement()! + alphabet.randomElement()!
Instead of writing many times the same code, try some loops:
let alphabet = ["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"]
var i = 0
var temp = ""
//The number of letters in alphabet equals 26
while i < 6 {
temp = temp + alphabet[Int.random(in: 0...25)]
i += 1
}
let password = temp
print(password)
beginner in swift with below solution
let passwordChrt:Array = [
"A", "a", "B", "b", "C" , "c", "D", "d", "E", "e", "F", "f", "G", "g", "H", "h",
"I", "i", "J", "j", "K", "k", "L", "l", "M", "m","N", "n", "O", "o", "P", "p",
"Q", "q", "R", "r", "S", "s", "T", "t", "U", "u", "V", "v", "W", "w","X", "x", "Y",
"y", "Z", "z", 1, 2, 3, 4, 5, 6, 7 , 8 ,9, 0, "!", "#", "#", "$", "%", "^", "&", "*"] as [Any]
var password:String = ""
passwordChrt.shuffled()
for _ in passwordChrt {
if password.count < 6 {
password.append("\(passwordChrt.randomElement() ?? <#default value#>)")
}
}
print(password)
Updated, newer version:
func randomPassword(length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyz"
return String((0..<length).map { _ in letters.randomElement()! })
}
print(randomPassword(length: 6)) // e.g. output: "deckro"
I just started with Ruby. I need to build a method that takes two letters as arguments and returns an array of two arrays containing the same two letters and the letters included between them. The first array should contain only vowels whereas the second array only consonants. E.g.:
def alphamek('a', 'd')
should return:
[['a'], ['b', 'c', 'd']]
I tried this:
def alphamek(letter1, letter2)
first_array = (letter1..letter2).scan[aeiou].to_a
second_array = (letter1..letter2).scan[^aeiou].to_a
multi_array = [[first_array], [second_array]]
end
but it doesn't seem to work. Any ideas?
Not really that hard if you work it from a regular expression perspective and leverage a tool like partition:
VOWEL = /[aeiou]/i
def alphamek(a, b)
(a..b).partition { |l| VOWEL.match(l) }
end
Another way of doing that is to use the methods Array#& and Array#-.
VOWELS = %w| a e i o u |
#=> [“a“, ”e”, ”i”, ”o”, ”u”]
def doit(first_letter, last_letter)
letters = (first_letter..last_letter).to_a
[VOWELS & letters, letters - VOWELS]
end
doit 'f', 't'
#=> [["i", "o"], ["f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t"]]
doit 'f', 'o'
#=> [["i", "o"], ["f", "g", "h", "j", "k", "l", "m", "n"]]
doit 'v', 'z'
#=> [[], ["v", "w", "x", "y", "z"]]
You are calling scan on the Range (letter1..letter2). That method does not exist.
What you can do is call select since Ranges are enumerable, see the documentation on an explanation for select: http://ruby-doc.org/core-2.5.0/Enumerable.html#method-i-select
Here's a working alternative that closely resembles your approach (as you intended it to work):
def alphamek(letter1, letter2)
vowels = 'aeiou'
# select all letters that are vowels
first_array = (letter1..letter2).select { |letter| vowels.include?(letter) }
# reject all letters that are vowels
second_array = (letter1..letter2).reject { |letter| vowels.include?(letter) }
return first_array, second_array # => [[...], [...]]
end
reject is simply the opposite of select, I prefer to use it instead of inverting the condition.
Anyway, there is an even better approach to this partitioning:
def alphamek(letter1, letter2)
vowels = 'aeiou'
(letter1..letter2).partition { |letter| vowels.include?(letter) }
end
This does the same as the other approach. partition splits the enumerable into two arrays, the first one contains the values for which the block evaluates to true, the second those for which it evaluates to false.
See partition in the docs: http://ruby-doc.org/core-2.5.0/Enumerable.html#method-i-partition
I have a string in this format:
"dataA\r\r\n" + "dataB\r\r\n" + "dataC\r\r\n" + "dataD"
The last data doesn't have "\r\r\n".
I would like to parse it and change to it an array of strings:
["dataA", "dataB", "dataC", "dataD"]
I am wondering what is the best way to do this.
You could split your concatenated string by passing \r\r\n which is a common point between each value:
a = "dataA\r\r\n" + "dataB\r\r\n" + "dataC\r\r\n" + "dataD"
p a.split(/\r\r\n/)
# => ["dataA", "dataB", "dataC", "dataD"]
str = "dataA\r\r\n" + "dataB\r\r\n" + "dataC\r\r\n" + "dataD"
#=> "dataA\r\r\ndataB\r\r\ndataC\r\r\ndataD"
str.split
#=> ["dataA", "dataB", "dataC", "dataD"]
See the 4th paragraph of the doc for String#split. Ruby's definition of "whitespace" is given in the doc for String#strip. For
str = "a" + "b\t\t" + "c\n\n\n" + "d\v\v" + "e\f" + "f\r\r" + "g " + "h"
str.split
#=> ["ab", "c", "d", "e", "f", "g", "h"]
is therefore equivalent to
str.split(/[\t\n\v\f\r \0]+/)
#=> ["ab", "c", "d", "e", "f", "g", "h"]
Search for "Backslash Notation" here for definitions of these backslash characters: horizontal tab, line feed, vertical tab, form feed, carriage return, space.
Ruby 2.4. I have an array of strings
2.4.0 :007 > arr = ["a", "b", "g", "e", "f", "i"]
=> ["a", "b", "g", "e", "f", "h", "i"]
How do I split my array into smaller arrays based on a condition? I have a function -- "contains_vowel," which returns true if a string contains "a", "e", "i", "o", or "u". How would I take an array of strings and split it into smaller arrays, using a divider function of "contains_vowel"? That is, for the above, the resulting array of smaller arrays would be
[["a"], ["b", "g"], ["e"], ["f", "h"], ["i"]]
If an element of the larger array satisfies the condition, it would become an array of one element.
arr = ["a", "b", "g", "e", "f", "i"]
r = /[aeiou]/
arr.slice_when { |a,b| a.match?(r) ^ b.match?(r) }.to_a
=> [["a"], ["b", "g"], ["e"], ["f"], ["i"]]
String#match? made its debut in Ruby v2.4. For earlier versions you could use (for example) !!(b =~ r), where !! converts a truthy/falsy value to true/false. That converstion is needed because the XOR operator ^ serves double-duty: it's a logical XOR when a and b in a^b are true, false or nil, and a bit-wise XOR when the operands are integers, such as 2^6 #=> 4 (2.to_s(2) #=> "10"; 6.to_s(2) #=> "110"; 4.to_s(2) #=> "100").
One more way to skin a cat
def contains_vowel(v)
v.count("aeiou") > 0
end
def split_by_substring_with_vowels(arr)
arr.chunk_while do |before,after|
!contains_vowel(before) & !contains_vowel(after)
end.to_a
end
split_by_substring_with_vowels(arr)
#=> [["a"], ["b", "g"], ["e"], ["f", "h"], ["i"]]
What it does:
passes each consecutive 2 elements
splits when either of them contain vowels
Example with your other Array
arr = ["1)", "dwr", "lyn,", "18,", "bbe"]
split_by_substring_with_vowels(arr)
#=> [["1)", "dwr", "lyn,", "18,"], ["bbe"]]
Further example: (if you want vowel containing elements in succession to stay in the same group)
def split_by_substring_with_vowels(arr)
arr.chunk_while do |before,after|
v_before,v_after = contains_vowel(before),contains_vowel(after)
(!v_before & !v_after) ^ (v_before & v_after)
end.to_a
end
arr = ["1)", "dwr", "lyn,", "18,", "bbe", "re", "rr", "aa", "ee"]
split_by_substring_with_vowels(arr)
#=> [["1)", "dwr", "lyn,", "18,"], ["bbe", "re"], ["rr"], ["aa", "ee"]]
This checks if before and after are both not vowels Or if they both are vowels
I might use chunk which splits an array everytime the value of its block changes. Chunk returns a list of [block_value, [elements]] pairs, I used .map(&:last) to only get the sub-lists of elements.
arr = ["a", "b", "g", "e", "f", "h", "i"]
def vowel?(x); %w(a e i o u).include?(x); end
arr.chunk{|x| vowel?(x)}.map(&:last)
=> [["a"], ["b", "g"], ["e"], ["f", "h"], ["i"]]
contains_vowel = ->(str) { !(str.split('') & %w|a e i o u|).empty? }
_, result = ["a", "b", "g", "e", "f", "h", "i"].
each_with_object([false, []]) do |e, acc|
cv, acc[0] = acc[0], contains_vowel.(e)
cv ^ acc.first ? acc.last << [e] : (acc.last[-1] ||= []) << e
end
result
#⇒ [["a"], ["b", "g"], ["e"], ["f", "h"], ["i"]]
What we do here:
contains_vowel is a lambda to check whether the string contains a vowel or not.
we reduce the input array, collecting the last value (contained the previously handled string the vowel or not,) and the result.
cv ^ acc.first checks whether it was a flip-flop of vowel on the last step.
whether is was, we append a new array to the result
whether is was not, we append the string to the last array in the result.
I am having a problem understanding how array.sort{ |x,y| block } works exactly, hence how to use it?
An example from Ruby documentation:
a = [ "d", "a", "e", "c", "b" ]
a.sort #=> ["a", "b", "c", "d", "e"]
a.sort { |x,y| y <=> x } #=> ["e", "d", "c", "b", "a"]
In your example
a.sort
is equivalent to
a.sort { |x, y| x <=> y }
As you know, to sort an array, you need to be able to compare its elements (if you doubt that, just try to implement any sort algorithm without using any comparison, no <, >, <= or >=).
The block you provide is really a function which will be called by the sort algorithm to compare two items. That is x and y will always be some elements of the input array chosen by the sort algorithm during its execution.
The sort algorithm will assume that this comparison function/block will meet the requirements for method <=>:
return -1 if x < y
return 0 if x = y
return 1 if x > y
Failure to provide an adequate comparison function/block will result in array whose order is undefined.
You should now understand why
a.sort { |x, y| x <=> y }
and
a.sort { |x, y| y <=> x }
return the same array in opposite orders.
To elaborate on what Tate Johnson added, if you implement the comparison function <=> on any of your classes, you gain the following
You may include the module Comparable in your class which will automatically define for you the following methods: between?, ==, >=, <, <= and >.
Instances of your class can now be sorted using the default (ie without argument) invocation to sort.
Note that the <=> method is already provided wherever it makes sense in ruby's standard library (Bignum, Array, File::Stat, Fixnum, String, Time, etc...).
When you have an array of, let's say, integers to sort, it's pretty straightforward for sort method to order the elements properly - smaller numbers first, bigger at the end. That's when you use ordinary sort, with no block.
But when you are sorting other objects, it may be needed to provide a way to compare (each) two of them. Let's say you have an array of objects of class Person. You probably can't tell if object bob is greater than object mike (i.e. class Person doesn't have method <=> implemented). In that case you'd need to provide some code to explain in which order you want these objects sorted to sort method. That's where the block form kicks in.
people.sort{|p1,p2| p1.age <=> p2.age}
people.sort{|p1,p2| p1.children.count <=> p2.children.count}
etc. In all these cases, sort method sorts them the same way - the same algorithm is used. What is different is comparison logic.
#OscarRyz reply cleared up a lot for me on the question on how the sort works, esp
{ |x, y| y <=> x }
Based on my understanding I am providing here what the state of the array would be after each comparison for above block results.
Note: Got the reference of printing the values of block paramaters e1, e2 from
ruby-forum
1.9.3dev :001 > a = %w(d e a w f k)
1.9.3dev :003 > a.sort { |e1, e2| p [e2, e1]; e2 <=> e1 }
["w", "d"]
["k", "w"]
["k", "d"]
["k", "e"]
["k", "f"]
["k", "a"]
["f", "a"]
["d", "f"]
["d", "a"]
["d", "e"]
["e", "f"]
=> ["w", "k", "f", "e", "d", "a"]
A guessed array state at runtime after each comparison:
[e2, e1] Comparsion Result Array State
["w", "d"] 1 ["w", "e", "a", "d", "f", "k"]
["k", "w"] -1 ["w", "e", "a", "d", "f", "k"]
["k", "d"] 1 ["w", "e", "a", "k", "f", "d"]
["k", "e"] 1 ["w", "k", "a", "e", "f", "d"]
["k", "f"] 1 ["w", "k", "a", "e", "f", "d"]
["k", "a"] 1 ["w", "k", "a", "e", "f", "d"]
["f", "a"] 1 ["w", "k", "f", "e", "a", "d"]
["d", "f"] -1 ["w", "k", "f", "e", "a", "d"]
["d", "a"] 1 ["w", "k", "f", "e", "d", "a"]
["d", "e"] -1 ["w", "k", "f", "e", "d", "a"]
["e", "f"] -1 ["w", "k", "f", "e", "d", "a"] (Result)
Thanks,
Jignesh
<=> is a method is ruby that returns ( self.<=>( argument ) )
-1 if self < argument
0 if self == argument
1 if self > argument
x and y are items of array. If no block is provided, the sort function uses x<=>y, otherwise the result of the block says if x should be before y.
array.sort{|x, y| some_very_complicated_method(x, y) }
Here if some_very_complicated_method(x, y) returns smth that is < 0, x is considered < than y and so on...
Some miscellaneous points:
x and y are called block parameters. The sort method basically says "I'll give you x and y, you determine whether x or y should come first, and I'll look after the boring stuff with regards to sorting"
<=> is called a spaceship operator.
In:
a.sort {|x,y| y <=> x } #=> ["e", "d", "c", "b", "a"]
what is x and y?
x and y are the elements being compared by the sorting algorithm.
This is useful to define for custom classes which element should be before the other.
For basic data ( numbers, strings , date, etc ) the natural order is predefined, but for customer element ( ie Employee ) you define who goes before who in a comparison. This block give you the chance to define that.
and what happens at y<=>x?
There, they are comparing the elements in descending order ( those with "higher" value will go first ) rather than the natural order ( x<=>y )
The <=> method stands for "compareTo" and return 0 if the elements are equivalent, or < 0 if x goes before than y or > 0 if x goes after y
I believe |x,y| y<=>x is comparing two elements at a time in descending order, as seen in:
http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-3C-3D-3E
Say with [ "d", "a", "e", "c", "b" ], "d" and "a" appear to be compared first. Then since it is descending, both remain in the same order because d evaluates to less than a. Then d and e are evaluated. "e" is moved to "d"'s position. Without knowing the internal workings of the c code it is not possible to know where is d moved to but I figure this process continues until all elements are sorted. The c functions:
VALUE
rb_ary_cmp(VALUE ary1, VALUE ary2)
{
long len;
VALUE v;
ary2 = rb_check_array_type(ary2);
if (NIL_P(ary2)) return Qnil;
if (ary1 == ary2) return INT2FIX(0);
v = rb_exec_recursive_paired(recursive_cmp, ary1, ary2, ary2);
if (v != Qundef) return v;
len = RARRAY_LEN(ary1) - RARRAY_LEN(ary2);
if (len == 0) return INT2FIX(0);
if (len > 0) return INT2FIX(1);
return INT2FIX(-1);
}