Swift: How to replace uppercase symbols in array - arrays

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

Related

Get items with the same position from multidimensional array in Swift 5

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

Swift - Find Specific Differences in Arrays of Structs

If I have a structure, say:
struct Subject {
var subjectID: String?
var name: String?
var note: String?
}
And I have two arrays of this structure: Array1 and Array2.
For example:
Array1 = [(subjectID = "T", name = "H", note = "DF"), (subjectID = "F", name = "H", note = "SD")]
Array2 = [(subjectID = "T", name "G", note = "DF"), (subjectID = "R", name = "F", note = "SDF")]
I want to return a new array, which consists of a subset of elements from Array2 that match the subjectID field of Array1 but have different name and/or note elements.
In the example above, the returned array would be:
[(subjectID = "T", name "G", note = "DF")]
As it contains the same subjectID (in this case T) as in Array1 but the name field is different. Note that the fields for this new returned array should be original values from Array2 (ex: you don't need to correct them to match Array1)
Is there an easy way (ex: one-two lines of code) to do this without brute forcing it?
Thanks!
There are good answers here, I prefer to keep the test simple.
First the setup
struct Subject {
var subjectID: String?
var name: String?
var note: String?
}
let array1 = [Subject(subjectID: "T", name: "H", note: "DF"), Subject(subjectID: "F", name: "H", note: "SD")]
let array2 = [Subject(subjectID: "T", name: "G", note: "DF"), Subject(subjectID: "R", name: "F", note: "SDF")]
Now lets look at the actual algorithm. array2.filter returns an array of Subjects in the array2 in which the block returns true. array1.contains returns true if any of the Subjects in array1 returns true. The test itself is exactly what you described. Are the subject id equal and does either the name or the note differ.
let result = array2.filter { s2 in
array1.contains { s1 in
s1.subjectID == s2.subjectID && (s1.name != s2.name || s1.note != s2.note)
}
}
You can do it like this:
let subjectsByID = Dictionary(grouping: array1, by: { $0.subjectID })
let diff = array2.filter { subject in
if let other = subjectsByID[subject.subjectID]?.first {
return subject.name != other.name || subject.note != other.note
} else {
return false
}
}
It groups the subjects in the first array by ID and then filters the second array based on whether or not there is an entry for that ID with a different name or note. You didn't specify what to do if there are multiple entries in the first array with the same ID so it just looks at the first one.
I used a forEach and filter in combination to find the requested elements
var result = [Subject]()
arr1.forEach( { subject in
result.append(contentsOf: arr2.filter( { $0.subjectID == subject.subjectID &&
($0.name != subject.name ||
$0.note != subject.note) }))
})
To get a little cleaner code the check could be made into a function in the struct
struct Subject {
...
func isModifiedComparedTo(_ subject: Subject) -> Bool {
return self.subjectID == subject.subjectID && (self.name != subject.name || self.note != subject.note)
}
}
var result = [Subject]()
arr1.forEach( { subject in
result.append(contentsOf: arr2.filter({$0.isModifiedComparedTo(subject)}))
})
You could filter the second array elements based on the first array elements, as:
let Array1 = [Subject(subjectID: "T", name: "H", note: "DF"), Subject(subjectID: "F", name: "H", note: "SD")]
let Array2 = [Subject(subjectID: "T", name: "G", note: "DF"), Subject(subjectID: "R", name: "F", note: "SDF")]
let result = Array2.filter { subject -> Bool in
for s in Array1 {
if subject.subjectID == s.subjectID && subject.name != s.name && subject.note != s.subjectID { return true }
}
return false
}
result should contains what are you asking for. Keep in mind that it has the complexity of nested iteration (O(n²)).

Accept Only Letter Characters for Signup (Swift) [duplicate]

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åäöé中國"

Get multiple values from an array?

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"

Xcode Swift check if array contains object

I have this array :
var preferiti : [ModalHomeLine!] = []
I want to check if the array contains the same object.
if the object exists {
} else {
var addPrf = ModalHomeLine(titolo: nomeLinea, link: linkNumeroLinea, immagine : immagine, numero : titoloLinea)
preferiti.append(addPrf)
}
Swift has a generic contains function:
contains([1,2,3,4],0) -> false
contains([1,2,3,4],3) -> true
So it sounds like you want an array without duplicate objects. In cases like this, a set is what you want. Surprisingly, Swift doesn't have a set, so you can either create your own or use NSSet, which would look something like this:
let myset = NSMutableSet()
myset.addObject("a") // ["a"]
myset.addObject("b") // ["a", "b"]
myset.addObject("c") // ["a", "b", "c"]
myset.addObject("a") // ["a", "b", "c"] NOTE: this doesn't do anything because "a" is already in the set.
UPDATE:
Swift 1.2 added a set type! Now you can do something like
let mySet = Set<String>()
mySet.insert("a") // ["a"]
mySet.insert("b") // ["a", "b"]
mySet.insert("c") // ["a", "b", "c"]
mySet.insert("a") // ["a", "b", "c"] NOTE: this doesn't do anything because "a" is already in the set.

Resources