Swift: Generate a set of (X) random 6 character substrings - arrays

I am trying to generate 5 random substrings of six characters each from the alphabet. For example: ABCDEF, RSTUVW, UVWXYZ, etc. These substrings can be duplicates, so generating ABCDEF twice is a not a problem.
When I have these 5 substrings, I want to generate five arrays containing three characters. One of these characters should be the last letter of the substring and the other two letters should be two random unique letters from the entire alphabet.
Example:
Get five random substrings:
[ABCDEF], [RSTUVW], [CDEFGH], [LMNOPQ], [UVWXYZ]
For [ABCDEF] the system could generate [F, H, S] and for [RSTUVW] it could generate [K, Q, W]. As you can see, the three-character arrays always contain the last letter of its substring and two other randomised unique letters.
The above is part of a game for kids to practice the order of the alphabet. In order to generate possible answers I actually need small sets of characters to assign to buttons.
What do you think is the best way to approach this?

Thanks to #vacawama here's a possible solution.
1. Create the alphabet
let alphabet = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
2. Create the 6 "sequences"
let sequences = (0..<5).map { _ -> String in
let startIndex = Int(arc4random_uniform(UInt32(alphabet.count - 5)))
let endIndex = startIndex + 5
return String(alphabet[startIndex...endIndex])
}
["PQRSTU", "DEFGHI", "JKLMNO", "CDEFGH", "KLMNOP"]
3. Get the last char for each sequence
let lastChars = sequences.flatMap { $0.characters.last }
["U", "I", "O", "H", "P"]
4. Build 5 elms
Here's the code snippet
let elms = lastChars.map { char0 -> String in
var tempAlphabet = alphabet
tempAlphabet.removeAtIndex(tempAlphabet.indexOf(char0)!)
let index1 = Int(arc4random_uniform(UInt32(tempAlphabet.count)))
let char1 = tempAlphabet.removeAtIndex(index1)
let index2 = Int(arc4random_uniform(UInt32(tempAlphabet.count)))
let char2 = tempAlphabet[index2]
return String(Array(Set<Character>([char0, char1, char2])))
}
["DUN", "ZIQ", "ROP", "HSW", "PGS"]
Update
Here's another solution to fix the problem hightligted by #dfri in the comments below. The following code snipped could replace the previous bullet 4.
extension Array {
mutating func removeRandom() -> Element {
let index = Int(arc4random_uniform(UInt32(count)))
return removeAtIndex(index)
}
}
var availableChars = Array(Set(alphabet).subtract(lastChars))
let elms = lastChars.map { String([$0, availableChars.removeRandom(), availableChars.removeRandom()]) }

Random substrings:
func randomString(length: Int) -> String {
let charactersString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let n = UInt32(charactersString.characters.count)
var out = ""
for _ in 0..<length {
let index = charactersString.startIndex.advancedBy(Int(arc4random_uniform(n)))
out.append(charactersString[index])
}
return out
}
Get 5 Strings:
func createSubstrings() -> [String] {
var array = [String]()
for _ in 0...5 {
array.append(self.randomString(6))
}
return array
}
Last array:
func createFinalStrings() -> [String] {
let substrings = createSubstrings()
var finalStrings = [String]()
let lastChars = substrings.flatMap { $0.characters.last }
for _ in 0...5 {
var string = ""
while string.characters.count < 3 {
let index = Int(arc4random_uniform(5))
let lastChar = lastChars[index]
if string.rangeOfString(String(lastChar)) == nil{
string = string + String(lastChar)
}
}
finalStrings.append(string)
}
return finalStrings
}

Related

Sorting string arrays in Swift

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sort(backwards)
print(reversed)
var ascending = names.sort({ (s1: String, s2: String) -> Bool in
return s1 < s2
})
print(ascending)
let sortAscending = { (s1: String, s2: String) -> Bool in
return s1 < s2
}
ascending = names.sort(sortAscending)
I am suppose to sort this code according to the number of characters they have from the most to the least. For example, Daniella has 8 characters so she will be fist in the list. The output I am suppose to get is
["Daniella", "Barry", "Chris", "Alex", "Ewa"]
Try this:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let sortedNames = names.sorted { $0.characters.count > $1.characters.count }
print(sortedNames)
Should I sort or should I sorted? Use sort to order the original array in-place, if declared as a var; if let your code won't even compile. Use sorted to leave your original array alone and return a new, properly sorted array; works on let and var.

swift: compare 2 arrays of characters, remove one value from each

Here's what I'm trying to do. I'm making a game where opponents guess each other's word, and each guess has direct and indirect hits. See asterisks for where I'm having an issue.
var directSum = 0
var indirectSum = 0
var words = ["canon", "cnams"]
var secretWord = Array(words[0].characters)
var guessWord = Array(words[1].characters)
var secretWordInd = [Character]()
var guessWordInd = [Character]()
let dir1 = secretWord[0] == guessWord[0]
let dir2 = secretWord[1] == guessWord[1]
let dir3 = secretWord[2] == guessWord[2]
let dir4 = secretWord[3] == guessWord[3]
let dir5 = secretWord[4] == guessWord[4]
if dir1 && dir2 && dir3 && dir4 && dir5 {
print ("you won!")
}
if dir1 {
directSum += 1
} else {
secretWordInd.append(secretWord[0])
guessWordInd.append(guessWord[0])
}
if dir2 {
directSum += 1
} else {
secretWordInd.append(secretWord[1])
guessWordInd.append(guessWord[1])
}
if dir3 {
directSum += 1
} else {
secretWordInd.append(secretWord[2])
guessWordInd.append(guessWord[2])
}
if dir4 {
directSum += 1
} else {
secretWordInd.append(secretWord[3])
guessWordInd.append(guessWord[3])
}
if dir5 {
directSum += 1
} else {
secretWordInd.append(secretWord[4])
guessWordInd.append(guessWord[4])
**}
for var secretLetter in secretWordInd {
for var guessLetter in guessWordInd{
if secretLetter == guessLetter {
secretWordInd.remove(at:secretWordInd.indexOf(secretLetter))
guessWordInd.remove(at:guessWordInd.indexOf(guessLetter))
indirectSum += 1
}
}
}**
var score = [directSum, indirectSum]
what I need to do is count every time there's a character in Array:SecretWordInd that matches a character in Array:guessWordInd, remove only those two characters(one from each string), and indirectSum += 1 for every time this occurs. If there are 2 "a"s in one and 1 in the other, for instance, the function needs to remove 1 a from each. That means the output of the function for directSum, indirectSum in this case should be [1,2] since there is only one indirect hit from one "n" from the guess word. This is what is making the function complicated for me. The number of values in both arrays will not be constant. I can't figure out how to use a method to do this(.contains is for only strings I think). Your help is greatly appreciated! Thanks!
You should look into Array, Set and String data types in the documentation, they provide lots of functions to perform that kind of calculations and transformations.
For example:
var secretWord = "canon"
var guessWord = "cnams"
// identify shared letters using sets
let sharedLetters = Set(secretWord.characters).intersection(Set(guessWord.characters))
// count number of shared letters present in both words
let sharedCounts = sharedLetters.map{ c in (c, secretWord.characters, guessWord.characters) }
.map{ (c,s,g) in (c, s.filter{$0==c}, g.filter{$0==c}) }
.map{ (c,s,g) in (c, min(s.count,g.count)) }
// count same letters at same position and shared
// (if you're making a mastermind game, you should subtract directCount from indirectCount)
let directCount = zip(secretWord.characters,guessWord.characters).filter{$0==$1}.count
let indirectCount = sharedCounts.map{$0.1}.reduce(0,+)
// remove shared letters (for count) from both strings
sharedCounts.flatMap{ Array(repeating:$0, count:$1) }
.forEach{ secretWord.remove(at:secretWord.characters.index(of:$0)!) }
sharedCounts.flatMap{ Array(repeating:$0, count:$1) }
.forEach{ guessWord.remove(at:guessWord.characters.index(of:$0)!) }
sharedLetters // {"n", "a", "c"}
sharedCounts // ("n", 1), ("a",1), ("c", 1)]
directCount // 1
indirectCount // 3
secretWord // "on"
guessWord // "ms"
As others have said this isn't the place for homework ;)
But here are some pointers as to what might be useful:
indexOf : returns index of element you are looking for, nil if not in set
remove : remove from an array

Swift 3 Find position of the biggest Double in array

My Array:
let array = [45,12,10,90]
// The number I need in this case is 3
Then I need to grab a value of another array:
let otherArray = [6,6,7,4,0]
I have tried to resolve the problem like this:
let maxPosition = array.max()
let desiredValue = otherArray[maxPosition]
This doesn't seem to work as desired.
Thanks for your help!
The problem there is that max returns the maximum value from your array, not an index. You need to find the index of the maximum value and use it with your other array:
let array = [45,12,10,90]
let otherArray = [6,6,7,4,0]
if let maxValue = array.max(), let index = array.index(of: maxValue) {
let desiredValue = otherArray[index]
print(desiredValue) // 4
}
Another option is to use your collection indices when getting the maximum value:
if let index = array.indices.max(by: { array[$0] < array[$1] }) {
let desiredValue = otherArray[index]
print(desiredValue) // 4
}
Here is another approach:
let array = [45,12,10,90]
let otherArray = [6,6,7,4,0]
var maxValueInArray = array[0]
for i in 1..<array.count{
if array[i] > maxValueInArray{
maxValueInArray = array[i]
}
}
if let maxValueIndex = array.index(of: maxValueInArray){
let desiredValueInOtherArray = otherArray[maxValueIndex]
print("Maximum value in array is \(maxValueInArray) with index \(maxValueIndex). Value in otherArray under index \(maxValueIndex) is \(desiredValueInOtherArray)")
}
You could find the index of the highest value by comparing the elements in the array to its highest value:
let array = [45,12,10,90]
let otherArray = [6,6,7,4,0]
if let index = array.firstIndex(where: { $0 == array.max() }) {
print(otherArray[index]) //4
}
Just be mindful that the two arrays are of different sizes, therefore the index of the highest value in one array could be out-of-bounds in the other array.

How to compare each letters in an array with all the letters in another array? - Swift

i have 2 different arrays called: criptedChar and alphabet. I need to check the first character in criptedChar (so "criptedchar[0]) and check a correspondence in alphabet.
For exemple:
criptedChar // ["d","e","c","b"]
alphabet // ["a","b","c" and so on]
I want to take d from criptedChar[0] and check if there's a "d" in all alphabet and then save the position of "d" in the second array.
I also need to increment the number inside the parenthesis of criptedChar. I'll take the number from the user.
Can you please help me? Thank you!
func decript() {
var criptedText = incriptedText.text! //get text from uiTextField
var criptedChar = Array<Character>(criptedText.characters) //from text to char & all in array :D
var alfabeto: Array<Character> = ["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 capacityCriptedCharArray = criptedChar.capacity
for (var i = 0; i < 26; i++) {
if criptedChar[0] == alfabeto[i] {
decriptedText.text = decriptedText.text! + "\(newLettersFromSecondViewController[i])"
}
}
for (var i = 0; i < 26; i++) {
if criptedChar[1] == alfabeto[i] {
decriptedText.text = decriptedText.text! + "\(newLettersFromSecondViewController[i])"
}
}
for (var i = 0; i < 26; i++) {
if criptedChar[2] == alfabeto[i] {
decriptedText.text = decriptedText.text! + "\(newLettersFromSecondViewController[i])"
}
}
}
This code works, but it's dumb and i have no control of the user input
If I understand your question correctly, you are looking for
something like this (explanations inline):
// Start with your crypted text, and an empty string for the result:
let cryptedText = "mifpyx"
var decryptedText = ""
// Two character arrays (of equal length):
let alphabet = Array("abcdefghijklmnopqrstuvwxyz".characters)
let newLetters = Array("ghijklmnopqrstuvwxyzabcdef".characters)
// For each character in the input string:
for c in cryptedText.characters {
// Check if `c` is contained in the `alphabet` array:
if let index = alphabet.indexOf(c) {
// Yes, it is, at position `index`!
// Append corresponding character from second array to result:
decryptedText.append(newLetters[index])
}
}
print(decryptedText) // solved
Alternatively, you can create a lookup-dictionary from the
two arrays:
var mapping = [ Character : Character ]()
zip(alphabet, newLetters).forEach {
mapping[$0] = $1
}
and then map each character from the input through that
dictionary:
let decryptedText = Array(cryptedText.characters
.map { mapping[$0] }
.flatMap { $0 }
)
(Here flatMap is used to filter-out the nils from characters which are not present in the input array.)
Here are examples of simple cypher logic that you can probably adapt to your application:
let readableText = "the quick brown fox jumped over the lazy dog"
// letters: letters in readable text that will be encoded
// cypher : corresponding encoded letters
//
// note: letters and cypher must have the same number of elements
let letters:[Character] = ["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 cypher:[Character] = ["o","p","q","r","a","b","c","d","e","f","g","h","i","u","v","w","x","y","z","j","k","l","m","n","s","t"]
// build a mapping disctionary from readable to encoded
var encode:[Character:Character] = [:]
for (index, letter) in letters.enumerate() { encode[letter] = cypher[index] }
// encrypt the readble text gives: "jda xkeqg pyvmu bvn fkiwar vlay jda hots rvc"
let cryptedText = String(readableText.characters.map({ encode[$0] ?? $0 }))
// build a mapping disctionary from encoded to readable
var decode:[Character:Character] = [:]
for (index, letter) in cypher.enumerate() { decode[letter] = letters[index] }
// decrypted the encrypted text gives: "the quick brown fox jumped over the lazy dog"
let decryptedText = String(cryptedText.characters.map({ decode[$0] ?? $0 }))

Most common array elements

I need to find the most common (modal) elements in an array.
The simplest way I could think of was to set variables for each unique element, and assign a count variable for each one, which increases every time it is recorded in a for loop which runs through the array.
Unfortunately the size of the array is unknown and will be very large, so this method is useless.
I have come across a similar question in Objective-C that uses an NSCountedSet method to rank the array elements. Unfortunately I am very new to programming, and could only translate the first line into Swift.
The suggested method is as follows:
var yourArray: NSArray! // My swift translation
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:yourArray];
NSMutableDictionary *dict=[NSMutableDictionary new];
for (id obj in set) {
[dict setObject:[NSNumber numberWithInteger:[set countForObject:obj]]
forKey:obj]; //key is date
}
NSLog(#"Dict : %#", dict);
NSMutableArray *top3=[[NSMutableArray alloc]initWithCapacity:3];
//which dict obj is = max
if (dict.count>=3) {
while (top3.count<3) {
NSInteger max = [[[dict allValues] valueForKeyPath:#"#max.intValue"] intValue];
for (id obj in set) {
if (max == [dict[obj] integerValue]) {
NSLog(#"--> %#",obj);
[top3 addObject:obj];
[dict removeObjectForKey:obj];
}
}
}
}
NSLog(#"top 3 = %#", top3);
In my program I will need to find the top five place names in an array.
edit: now with Swift 2.0 below
Not the most efficient of solutions but a simple one:
let a = [1,1,2,3,1,7,4,6,7,2]
var frequency: [Int:Int] = [:]
for x in a {
// set frequency to the current count of this element + 1
frequency[x] = (frequency[x] ?? 0) + 1
}
let descending = sorted(frequency) { $0.1 > $1.1 }
descending now consists of an array of pairs: the value and the frequency,
sorted most frequent first. So the “top 5” would be the first 5 entries
(assuming there were 5 or more distinct values). It shouldn't matter how big the source array is.
Here's a generic function version that would work on any sequence:
func frequencies
<S: SequenceType where S.Generator.Element: Hashable>
(source: S) -> [(S.Generator.Element,Int)] {
var frequency: [S.Generator.Element:Int] = [:]
for x in source {
frequency[x] = (frequency[x] ?? 0) + 1
}
return sorted(frequency) { $0.1 > $1.1 }
}
frequencies(a)
For Swift 2.0, you can adapt the function to be a protocol extension:
extension SequenceType where Generator.Element: Hashable {
func frequencies() -> [(Generator.Element,Int)] {
var frequency: [Generator.Element:Int] = [:]
for x in self {
frequency[x] = (frequency[x] ?? 0) + 1
}
return frequency.sort { $0.1 > $1.1 }
}
}
a.frequencies()
For Swift 3.0:
extension Sequence where Self.Iterator.Element: Hashable {
func frequencies() -> [(Self.Iterator.Element,Int)] {
var frequency: [Self.Iterator.Element:Int] = [:]
for x in self {
frequency[x] = (frequency[x] ?? 0) + 1
}
return frequency.sorted { $0.1 > $1.1 }
}
}
For XCode 7.1 the solution is.
// Array of elements
let a = [7,3,2,1,4,6,8,9,5,3,0,7,2,7]
// Create a key for elements and their frequency
var times: [Int: Int] = [:]
// Iterate over the dictionary
for b in a {
// Every time there is a repeat value add one to that key
times[b] = (times[b] ?? 0) + 1
}
// This is for sorting the values
let descending = times.sort({$0.1 > $1.1})
// For sorting the keys the code would be
// let descending = times.sort({$0.0 > $1.0})
// Do whatever you want with sorted array
print(descending)
Same as Airspeed Velocity, using a reduce instead of for-in:
extension Sequence where Self.Iterator.Element: Hashable {
func frequencies() -> [(Self.Iterator.Element, Int)] {
return reduce([:]) {
var frequencies = $0
frequencies[$1] = (frequencies[$1] ?? 0) + 1
return frequencies
}.sorted { $0.1 > $1.1 }
}
}
But please note that, here, using reduce with a struct is not as efficient as a for-in because of the struct copy cost. So you will generally prefer the for-in way of doing it.
[edit: gosh, the article is by the same guy as the top answer!]

Resources