Swift Array - Difference between "Int.random(in: 0...25)" and "randomElement()" - arrays

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"

Related

How to Sort Everything in an Array Except Special Chars - Ruby

I am trying to sort an array of letters alphabetically, but keep the special character in the same spot, in ruby.
For example,
word = ["f", "a", "s", "t", "-", "c", "a", "r", "s"]
How can I sort the whole thing alphabetically, but keep the "-" where it is. If I sort how it is now then the "-" will go to the front which I don't want. I have tried fifteen different ways, and I can't figure it out. Can you help?
Some really verbose way to do this, to explain the logic around what you need to achieve. There's some 'cleaner' methods to achieve this, but I feel this gives more understanding.
Adding an additional special character to the array for better test coverage:
let(:input) { ["f", "a", "s", "t", "-", "c", "a", "r", "s", "/"] }
let(:desired_output) { ["a", "a", "c", "f", "-", "r", "s", "s", "t", "/"] }
it "takes the input and gives the desired output" do
expect(sort_alphanumeric_characters(input)).to eq(desired_output)
end
Call .map and .select on the array to enumerate over the values and then call .with_index as you'll need to retain indicies for later.
def sort_alphanumeric_characters(word_as_array)
# assuming you mean non-alphanumeric
# collect those indicies which are 'special' characters
# the regex matches the string with anything outside of the alphanumeric range. Note the '^'
special_character_indicies = word_as_array.map.with_index { |val, indx| indx if val =~ /[^a-zA-Z0-9]/ }.compact
# collect all characters by index that were not yielded as 'special'
alphanumeric_array = word_as_array.select.with_index { |char, indx| char unless special_character_indicies.include? indx }
# sort the alphanumeric array
sorted_alphanumeric_array = alphanumeric_array.sort
# use Array#insert to place the 'special' by index
special_character_indicies.each do |special_indx|
special_char = word_as_array[special_indx]
sorted_alphanumeric_array.insert(special_indx, special_char)
end
# return desired output
sorted_alphanumeric_array
end
As soon as I posted I had a lightning bolt (love it when that happens). This is really not that great of a solution, but it did work!!
def scramble_words(str)
idx = 0
chars = str.delete("^a-z").chars
first_ele = chars.shift
last_ele = chars.pop
str.chars.each_with_index {|c, i| idx = i if c =~ /[^a-z" "]/ }
(first_ele + chars.sort.join + last_ele).insert(idx, str[idx])
end
p scramble_words('card-carrying') == 'caac-dinrrryg'
p scramble_words("shan't") == "sahn't"
p scramble_words('-dcba') == '-dbca'

Concatenate values returned from an array in Swift

I'm training on Swift, doing some exercises with arrays. I would like to pick up randomly 6 letters in an array containing the 26 letters of the alphabet, then concatenate them to generate a string.
I have no problem picking the values, but when it comes to concatenate them, I get error messages.
Here is my code :
var 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 a = alphabet.randomElement()
var b = alphabet.randomElement()
var c = alphabet.randomElement()
var d = alphabet.randomElement()
var e = alphabet.randomElement()
var f = alphabet.randomElement()
var password = a + b + c + d + e + f
print(password)
What am I doing wrong ?
Your alphabet is currently an [String] (a.k.a. Array<String>). You should probably change it to an [Character] instead.
Even better, since randomElement is defined on Collection, and Collection whose Element is Character would work. String is a Collection, and its Element is Character, so it fits the bill perfectly. The nicest way to do that is by breaking apart a string literal:
let alphabet = "abcdefghijklmnopqrstuvwxyz"
randomElement() returns a T? (in this case, String?, a.k.a. Optional<String>), not just a T. Because if alphabet were to be empty (it's not, but if it was), then there would be no elements to return. The only sane thing to do is return nil instead.
Since in your case you can be certain that alphabet is non-empty, and that randomElement() will always return a valid non element, you're justified to force unwrap the result with the ! operator. Now don't make a bad habit of this. You have good reason to force unwrap here, but it's not to be used as a first-resort method of optional handling.
You're also repeating yourself a lot. You can improve this by using a loop of some kind or another.
In this case, you can use a neat little trick. Starting with a range like 0..<6, which has 6 elements, you can call map on it to transform its elements (which are 0, 1, ... 5). In this case, you woudl transform each element by ignoring it, and just replacing it with a random element frmo the alphabet:
(0..<6).map { _ in alphabet.randomElement()! }
The result is an Array<Character>. All you need is to turn it into a string. Final code:
let alphabet = "abcdefghijklmnopqrstuvwxyz"
let password = String((0..<6).map { _ in alphabet.randomElement()! })
a, b, c, d, e, f are of type String?. So you need to unwrap them either using if-let or force-unwrapping before, i.e.
Using force-unwrapping,
let a = alphabet.randomElement()!
let b = alphabet.randomElement()!
let c = alphabet.randomElement()!
let d = alphabet.randomElement()!
let e = alphabet.randomElement()!
let f = alphabet.randomElement()!
Using if-let,
if let a = alphabet.randomElement(), let b = alphabet.randomElement(), let c = alphabet.randomElement(), let d = alphabet.randomElement(), let e = alphabet.randomElement(), let f = alphabet.randomElement() {
let password = a + b + c + d + e + f
print(password)
}
Using if-let is safer than force-unwrapping.
Note: Use let instead of var when using constant values.
Actually I think the simplest solution is to declare an extension at the top level of your file, allowing you to concatenate Optional Strings coherently:
func +(lhs:String?, rhs:String?) -> String {
return (lhs ?? "") + (rhs ?? "")
}
Now your original code will compile and run correctly without changes (except that I changed var to let everywhere to eliminate compiler warnings):
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"]
let a = alphabet.randomElement()
let b = alphabet.randomElement()
let c = alphabet.randomElement()
let d = alphabet.randomElement()
let e = alphabet.randomElement()
let f = alphabet.randomElement()
let password = a + b + c + d + e + f
print(password) // cpndmz
you can try this method
func randomPassword(digit : Int) -> String {
var password = ""
for _ in 0 ..< digit {
let number = Int.random(in: 97...122)
password += "\(UnicodeScalar(number)!)"
}
return password.uppercased()
}
let password = randomPassword(digit: 6)
print(password)
As others already mentioned, randomElement() can return nil as the reference says:
An easy solution will be like the following.
let 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)]
In this way, basically you tell swift that you want to handle the array out of range error.
Instead of creating all these a, b, c, d, e, f variables, you can simply use reduce(_:_:) to get the expected result.
let password = (1...6).reduce("") { (result, _) -> String in
return result + (alphabet.randomElement() ?? "")
}

How to Concatenate strings in Array using Ruby

I have following sveral arrays which each of them consists into a String.
x = ["t", "o", "d", "a", "y"]
y = ["i", "s"]
z = ["s", "u", "n", "d", "a", "y"]
my output should be like following:
x = [today]
y = [is]
Z = [sunday]
in together: today is sunday
How can i get expected array using ruby?
You will want to use the #join(separator) method.
See the official ruby docs for Array#join
Example:
['h', 'e', 'l', 'l', 'o'].join('')
=> "hello"
A good place to start learning the basics of Ruby is at Code Academy.
I also recommend dash for browsing documentation offline!
For final your output,
[x, y, z].map(&:join).join(' ')
You can use the .join() method like this:
x = ["t", "o", "d", "a", "y"]
y = ["i", "s"]
z = ["s", "u", "n", "d", "a", "y"]
x.join()
=> "today"
y.join()
=> "is"
z.join()
=> "sunday"
Then do this:
x.join + y.join + z.join()
=> "todayissunday"
Or combine x, y, z into one array and call join on it, like this:
Array(x + y + z).join
=> "todayissunday"

Parsing string to an array of strings in Ruby

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.

Character Array to string in Swift

The output of the [Character] Array currently is:
["E", "x", "a", "m", "p", "l", "e"]
It should be:
Example
It could be that " is in the array, like this: """. That output should be ".
Thank you!
The other answer(s) cover the case where your array is one of String elements (which is most likely the case here: since you haven't supplied us the type of the array, we could use Swift own type inference rules to speculatively infer the type to be [String]).
In case the elements of your array are in fact of type Character, however, you could use the Character sequence initializer of String directly:
let charArr: [Character] = ["E", "x", "a", "m", "p", "l", "e"]
let str = String(charArr) // Example
W.r.t. your comment below: if your example array is, for some reason, one of Any elements (which is generally not a good idea to use explicitly, but sometimes the case when recieving data from some external source), you first need to perform an attempted conversion of each Any element to String type, prior to concenating the converted elements to a single String instance. After the conversion, you will be working with an array of String elements, in which case the methods shown in the other answers will be the appropriate method of concenation:
// e.g. using joined()
let arr: [Any] = ["E", "x", "a", "m", "p", "l", "e"]
let str = arr.flatMap { $0 as? String }.joined()
print(str) // example
You could naturally also (attempt to) convert from Any to Character elements, but even in this case you would have to go via String instances, which means that for the [Any] case, the joined() alternative above is to prefer over the one below:
let arr: [Any] = ["E", "x", "a", "m", "p", "l", "e"]
let str = String(arr.flatMap { ($0 as? String)?.characters.first })
print(str) // example
Just use joined() with default "" separator:
let joinedString = ["E", "x", "a", "m", "p", "l", "e"].joined()
let e = ["E", "x", "a", "m", "p", "l", "e"]
print(e.reduce ("", +))

Resources