I'm converting a python programme into swift, and one section uses a for loop to keep every character within a string if it is a letter. In python, it's as simple as using '.isalpha()', is there anything in swift that does this? Code in python:
word = 'hello 123'
new_word = []
for i in word:
if i.isalpha():
new_word.append(i)
Xcode 8 beta4 • Swift 3
extension String {
var lettersOnly: String {
return components(separatedBy: CharacterSet.letters.inverted).joined(separator:"")
}
var lettersArray: [Character] {
return Array(lettersOnly.characters)
}
}
Xcode 7.3.1 • Swift 2.2.1
extension String {
var lettersOnly: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet.letterCharacterSet().invertedSet).joinWithSeparator("")
}
var lettersArray: [Character] {
return Array(lettersOnly.characters)
}
}
Swift 1.x
import UIKit
extension String {
var lettersOnly: String {
return "".join(componentsSeparatedByCharactersInSet(NSCharacterSet.letterCharacterSet().invertedSet))
}
var lettersArray: [Character] {
return Array("".join(componentsSeparatedByCharactersInSet(NSCharacterSet.letterCharacterSet().invertedSet)))
}
}
let word = "hello 123"
let letters = word.lettersOnly // "hello"
let lettersArray = word.lettersArray // ["h", "e", "l", "l", "o"]
Why not just:
let alphabet: [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 word = "h4e7l2l8o1"
var answer = [String]()
for letter in word {
if contains(alphabet, letter) {
answer.append(String(letter))
}
}
"".join(answer) // "hello"
or even:
let answer = "".join(filter(word) {contains(alphabet, $0)}.map{String($0)}) // "hello"
This should work:
import Foundation // For playground or REPL
let word = "This is a string !#€%!€#% 123456 åäö é 中國"
let letterCharSet = NSCharacterSet.letterCharacterSet().invertedSet
let newWord = "".join(word.componentsSeparatedByCharactersInSet(letterCharSet))
// Outputs: "Thisisastringåäöé中國"
Related
I have an array of strings I want to loop through and create a view for each element. To achieve that, I tried using ForEach(), the output of the code below are the following errors:
Cannot convert value of type '[String]' to expected argument type 'Binding<C>'
Generic parameter 'C' could not be inferred
Code:
struct HomeView: View {
let array: [String] = ["A", "B", "C", "D", "E", "F", "G"]
var body: some View {
VStack {
ForEach(array, id: \.self) { letter in
Text(array[letter])
}
}
}
}
PS: The code is simplified
Desired output:
VStack of all letters from the array
You can try this (just use the letter parameter from the for loop):
let array: [String] = ["A", "B", "C", "D", "E", "F", "G"]
var body: some View {
VStack {
ForEach(array, id: \.self) { letter in
Text(letter)
}
}
}
I can't find the best way to do this.
I have an array with 3 arrays in there(this never change)
var ancho = [String]()
var largo = [String]()
var cantidad = [String]()
var arrayDeCortes = [ancho,largo,cantidad]
arrayDeCortes = [[a,b,c,d,..],[e,f,g,h,..],[i,j,k,l,..]]
I need to get this:
[a,e,i]
[b,f,j]
[c,g,k]
[d,h,l]
My problem is that I don't know how many items there is in each array(ancho,largo,cantidad)
and how access to all of them.
I hope you understand me
You can use reduce(into:_:) function of Array like this:
let arrayDeCortes = [["a","b","c","d"],["e","f","g","h"],["i","j","k","l"]]
let arrays = arrayDeCortes.reduce(into: [[String]]()) { (result, array) in
array.enumerated().forEach {
if $0.offset < result.count {
result[$0.offset].append($0.element)
} else {
result.append([$0.element])
}
}
}
print(arrays)
// [["a", "e", "i"], ["b", "f", "j"], ["c", "g", "k"], ["d", "h", "l"]]
Edit: As #Alexander mentioned in the comments, there is a simpler way of achieving this by using zip(_:_:) function twice.
The following will return an array of tuples:
var widths = ["a","b","c","d"]
var heights = ["e","f","g","h"]
var quantities = ["i","j","k","l"]
let result = zip(widths, zip(heights, quantities)).map { width, pair in
(width, pair.0, pair.1)
}
print(result)
// [("a", "e", "i"), ("b", "f", "j"), ("c", "g", "k"), ("d", "h", "l")]
This is the code that replaces cyrillic symbols to latin:
let cyr = ["а", "б", "в", "г", "д", "Б"]
let latin = ["a", "b", "v", "g", "d", "B"]
var original = self.input.text
for i in 0..<cyr.count {
let target = cyr[i]
let destination = latin[i]
original = original?.replacingOccurrences(of: target, with: destination, options: String.CompareOptions(rawValue: NSString.CompareOptions.caseInsensitive.rawValue | NSString.CompareOptions.literal.rawValue), range: nil)
self.output.text = original;
But it doesn't replace uppercase symbols to upper if i add them in array. It either replaces all lowercases to upper, either upper to lower. How to make it differentiate if it's uppercase symbol or lower and replace it respectively?
You should not use the caseInsensitive option when you want a case sensitive search and replacement. Your code can also be simplified to
let cyr = ["а", "б", "в", "г", "д", "Б"]
let latin = ["a", "b", "v", "g", "d", "B"]
var text = "абвгдБ"
for (src, dest) in zip(cyr, latin) {
text = text.replacingOccurrences(of: src, with: dest)
}
print(text) // abvgdB
Having said that, what you really might want is a transliteration to latin:
let original = "абвгдБ"
let trans = original.applyingTransform(.toLatin, reverse: false) ?? original
print(trans) // abvgdB
Remove the caseInsensitive option and add the uppercase letters to the arrays.
let cyr = ["а", "б", "в", "г", "д", "Б", "А", ...]
let latin = ["a", "b", "v", "g", "d", "B", "A", ...]
var original = self.input.text
for i in 0..<cyr.count {
let target = cyr[i]
let destination = latin[i]
original = original?.replacingOccurrences(of: target, with: destination, options: [.literal], range: nil)
}
self.output.text = original
I am trying to get only 20 random values out of 26 from the lettersthat also have to include elements from the array name. finalArray would look like: ["S", "A", "M", "A", "N", "T", "H", "A", "I", "J", "K", "L", "S", "N", "O","P","Q", "R", "S", "A"](randomly)
So far:
var letters: [String] = ["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 name: [String] = ["S", "A", "M", "A", "N","T","H","A"]
//Create finalArray elements
var availableLetters = letters.filter { value in
!contains(name, value)
}
var finalArray = availableLetters + name
I tried to do:
//get 20 objects
var length = name.utf16Count
var beforeArray = finalArray[0...19]
//minus length of the word
var letterCount = beforeArray.count - length
// add missing letters
beforeArray = letters[0...letterCount] + name
Which is obviously wrong and the results are very unstable. What could I use as a simple workaround? How could I implement it?
It seems from your example that you want to take name, and then right-pad it with random letters from the alphabet up to a length of 20. Since it looks like you don’t mind about repeating the random letters, this makes things a lot easier.
The tricky part is generating a sequence of n random numbers up to a maximum. If you have this, you can use these numbers as indices into your alphabet array to pick the random characters. Here’s one way to generate that sequence:
// Struct representing a sequence of count
// random numbers in range 0..<max
struct RandomSequence: SequenceType {
let count: Int
let max: Int
func generate() -> GeneratorOf<Int> {
var i = 0
return GeneratorOf {
i++ < self.count
? Int(arc4random_uniform(UInt32(self.max)))
: nil
}
}
}
Once you have this, it can be used to generate the padding:
let name = Array("SAMANTHA")
let alphabet = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
let targetCount = 20
let paddingCount = targetCount - name.count
let ranseq = RandomSequence(count: paddingCount, max: alphabet.count)
let padding = map(ranseq) { alphabet[$0] }
let padded = name + padding
// padded = ["S", "A", "M", "A", "N", "T", "H", "A", "W", "O", "C", "M", "L", "B", "L", "A", "N", "H", "I", "I"]
If you actually want the name shuffled in with the random letters afterwards, look at the top answer to this question. But it’s probably easiest to do this as a second step to the above technique rather than try and combine both steps together.
It’s worth noting that if name is a string, and you want the result to end up as a string, you don’t need to put in an array for this approach to work:
let name = "SAMANTHA"
let alphabet = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
let targetCount = 20
let paddingCount = targetCount - count(name)
let ranseq = RandomSequence(count: paddingCount, max: alphabet.count)
let padding = map(ranseq) { alphabet[$0] }
let padded = name + padding
// padded = "SAMANTHASLHMPRDYRHFC"
Feel free to copy and paste the code into your fla. It should work to trace vars.
I am trying to create a kids matching game. It selects one letter for the alphabet and will ask them to find that letter from 3 choices. I am also going to randomize the 3 letters they pick from but it is not yet in this code.
My issue is most of the time it is removing an array var using "POP" but sometimes and I get DUPLICATES and sometimes it comes out NULL. What am I doing wrong here?
import flash.events.MouseEvent;
import flash.display.*;
/// Array of the Alphabet
var Alphabet:Array = ["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"];
// Arry to hold 3 unique letters
var randArray:Array = new Array();
function getRandomElementOf(array:Array):Object
{
var idx:int=Math.floor(Math.random() * array.length);
// Supposed to remove the letter so can't be chosen again
array.pop()
// Adds 1 of 3 letters to new array
randArray.push(array[idx]);
return array[idx];
}
function testArray(evt:MouseEvent){
var One = getRandomElementOf(Alphabet);
trace(One);
var Two = getRandomElementOf(Alphabet);
trace(Two);
var Three = getRandomElementOf(Alphabet);
trace(Three);
trace("Can you find the letter " + One + "? " + randArray);
// Resets the random Array
randArray = new Array();
// Resets the letters forto be chosen again.
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"];
}
/// button to click stage to test vars
stage.addEventListener(MouseEvent.CLICK, testArray);
An example shuffler, splicing letters from the alphabet collection:
var alphabet:Vector.<String> = new <String>[ "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" ];
while (alphabet.length > 0)
{
var letter:String = alphabet.splice(int(Math.random() *
alphabet.length), 1)[0];
trace(letter);
}
Example output:
V, M, F, E, D, U, S, L, X, K, Q, H, A, I, W, N, P, Y, J, C, T, O, R, G, B, Z
Applied to your example, here's a reset function to reset the alphabet collection back to its original state, a random letter function to remove a single letter from the alphabet collection, and a shuffle function to randomize the alphabet collection:
/** Alphabet collection */
var alphabet:Vector.<String>;
/** Reset alphabet */
function reset():void
{
alphabet = new <String>[ "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" ];
}
/** Get random letter from alphabet */
function getRandomLetter():String
{
return (alphabet.splice(int(Math.random() *
alphabet.length), 1)[0]);
}
/** Shuffle alphabet collection */
function shuffleAlphabet():Vector.<String>
{
var alphabetShuffled:Vector.<String> = new Vector.<String>();
while (alphabet.length > 0)
{
alphabetShuffled.push(getRandomLetter());
}
return alphabetShuffled;
}
The following pulls a random letter from the alphabet to be found and display the entire alphabet shuffled:
// get a random letter:
reset();
var randomLetter:String = getRandomLetter();
trace("Can you find the letter: " + randomLetter + "?");
// display entire alpha shuffled:
reset();
trace(shuffleAlphabet());
Example game output:
Can you find the letter: Q?
R,I,U,J,Y,D,K,W,T,F,N,G,A,P,X,H,Q,L,S,O,C,V,M,Z,E,B
Can you find the letter: P?
I,F,C,S,J,P,Q,M,D,T,H,X,O,V,W,G,K,A,N,Y,L,U,Z,R,B,E
Can you find the letter: S?
B,U,O,S,C,N,I,E,W,L,P,Q,Z,R,A,G,J,K,Y,M,T,V,X,D,H,F
array.splice()
and not array.pop();
I figured it out.