Get a value at index from range - swift

I want to retrieve a random emoji inside the range.
let emojiRanges = [
0x1F601...0x1F64F,
0x1F680...0x1F6C0,
]
let flattenEmoji = emojiRanges.flatten()
// the loop for emoji works
for i in flattenEmoji {
let st = String(format:"0x%2X %#", i, String(UnicodeScalar(i)))
print(st)
}
// but this is not possible to obtain value at wanted index
//there is a compiler error:
let randomSign = String(UnicodeScalar(flattenEmoji[arc4random_uniform(UInt32(flattenEmoji.count))]))
print("RANDOM \(randomSign)")
the error:
ViewController.swift:68:67: Cannot subscript a value of type
'FlattenBidirectionalCollection<[Range]>' (aka
'FlattenBidirectionalCollection>>') with an index of
type 'UInt32'
What is the proper way to get a result?

The problem is that flatten() is lazily applied, and therefore returns a special FlattenBidirectionalCollection, which is indexed by a FlattenBidirectionalCollectionIndex, rather than an Int.
The simplest solution therefore would be to simply use the Array(_:) constructor (or flatMap(_:)) in order to eagerly apply the flattening of the ranges, which will create an array that you can then subscript with an Int.
let flattenEmoji = Array(emojiRanges.flatten()) // In Swift 3, flatten() is named joined()
let randomIndex = Int(arc4random_uniform(UInt32(flattenEmoji.count)))
let randomSign = String(UnicodeScalar(flattenEmoji[randomIndex]))
If you wish to keep the flattening being lazily applied, you could subscript the FlattenBidirectionalCollection directly (for Swift 2) through using advancedBy(_:) on the collection's startIndex:
let randomIndex = flattenEmoji.startIndex.advancedBy(Int(arc4random_uniform(UInt32(flattenEmoji.count))))
let randomSign = String(UnicodeScalar(flattenEmoji[randomIndex]))
In Swift 3, as collections move their indices, you'd want use the collection's index(_:offsetBy:) method instead:
let randomIndex = flattenEmoji.index(flattenEmoji.startIndex, offsetBy: Int(arc4random_uniform(UInt32(flattenEmoji.count))))

Change emojiRanges declaration to this:
let emojiRanges = Array(0x1F601...0x1F64F) + Array(0x1F680...0x1F6C0)
then life will become much easier.
for i in emojiRanges {
let st = String(format:"0x%2X %#", i, String(UnicodeScalar(i)))
print(st)
}
in randomSign you should convert index to Int
let randomSign = String(UnicodeScalar(emojiRanges[Int(arc4random_uniform(UInt32(emojiRanges.count)))]))
print("RANDOM \(randomSign)")

Related

how to get the index value in dictionary using swift

As I am new to swift programming language .I am using the dictionary of two items now i need to take the index path of particular dictionary value .I am using the following code
var dictionaryitems = ["computer":"something to make work easy","pen":"used for writing something"]
print(dictionaryitems["pen"])
Use firstIndex for this
let index = dictionaryitems.firstIndex(where: {$0.key == "pen"})
you can get index of key or value by
let index = Array(Dictionary.keys).index(of: key/value)
by this you will get an optional value which you can unwrap using if-let or guard statement for further use
var dictionaryitems = ["computer":"something to make work easy","pen":"used for writing something"]
if let index = dictionaryitems.index(forKey: "pen") {
print(dictionaryitems[index].key, ":", dictionaryitems[index].value)
}
This is an example how you can get the index of a dictionary using swift >
if let index = carDataArray?.index(where: {$0["carName"] as! String == "BMW"}) {
print("Car Found")
}

Swift range: not including first element

For getting the elements in a certain range up to but excluding index, the following command is used:
let substr=mystr[..<index]
But how can I get a certain range, beginning from the first element after index? It should be like:
let substr=mystr[index<..] //does not work
You can use index(after:) method to get the following index:
let mystr = "Hello"
let index = mystr.startIndex
if index < mystr.endIndex {
let substr = mystr[mystr.index(after: index)...]
print(substr) // "ello\n"
}

Why does swift substring with range require a special type of Range

Consider this function to build a string of random characters:
func makeToken(length: Int) -> String {
let chars: String = "abcdefghijklmnopqrstuvwxyz0123456789!?##$%ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var result: String = ""
for _ in 0..<length {
let idx = Int(arc4random_uniform(UInt32(chars.characters.count)))
let idxEnd = idx + 1
let range: Range = idx..<idxEnd
let char = chars.substring(with: range)
result += char
}
return result
}
This throws an error on the substring method:
Cannot convert value of type 'Range<Int>' to expected argument
type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>')
I'm confused why I can't simply provide a Range with 2 integers, and why it's making me go the roundabout way of making a Range<String.Index>.
So I have to change the Range creation to this very over-complicated way:
let idx = Int(arc4random_uniform(UInt32(chars.characters.count)))
let start = chars.index(chars.startIndex, offsetBy: idx)
let end = chars.index(chars.startIndex, offsetBy: idx + 1)
let range: Range = start..<end
Why isn't it good enough for Swift for me to simply create a range with 2 integers and the half-open range operator? (..<)
Quite the contrast to "swift", in javascript I can simply do chars.substr(idx, 1)
I suggest converting your String to [Character] so that you can index it easily with Int:
func makeToken(length: Int) -> String {
let chars = Array("abcdefghijklmnopqrstuvwxyz0123456789!?##$%ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
var result = ""
for _ in 0..<length {
let idx = Int(arc4random_uniform(UInt32(chars.count)))
result += String(chars[idx])
}
return result
}
Swift takes great care to provide a fully Unicode-compliant, type-safe, String abstraction.
Indexing a given Character, in an arbitrary Unicode string, is far from a trivial task. Each Character is a sequence of one or more Unicode scalars that (when combined) produce a single human-readable character. In particular, hiding all this complexity behind a simple Int based indexing scheme might result in the wrong performance mental model for programmers.
Having said that, you can always convert your string to a Array<Character> once for easy (and fast!) indexing. For instance:
let chars: String = "abcdefghijklmnop"
var charsArray = Array(chars.characters)
...
let resultingString = String(charsArray)

How does one iterate over an Array of Ranges?

Swift 3's compiler will not let me compile the following:
let a = 0
let b = 10
var arr = [ClosedRange<Int>]()
let myRange: ClosedRange = a...b
arr.append(myRange)
for each in arr {
for every in each {
print(every)
}
}
...due to ClosedRange<Int> not conforming to the Sequence protocol. In the past, a simple extension to the class like so would have been enough:
extension ClosedRange<Int>: Sequence {}
...but now the compiler asks that the extension be declared with a where clause, which makes me think that I'm going about this all wrong. What am I missing?
The problem is not that you have an array of ranges, but that
ClosedRange in Swift 3 represents
An interval over a comparable type, from a lower bound up to, and including, an upper bound.
For example, a closed range can be used with Double
let r: ClosedRange<Double> = 1.1...2.2
where enumerating all possible values does not make much sense.
What you need is CountableClosedRange which is
A closed range that forms a collection of consecutive values.
and in particular is a collection and can be iterated over:
let a = 0
let b = 10
var arr = [CountableClosedRange<Int>]()
let myRange: CountableClosedRange = a...b
arr.append(myRange)
for each in arr {
for every in each {
print(every)
}
}
You can just write
let myRange = a...b
since by default, the ... operator produces a CountableClosedRange
if its operands are Strideable.
Similarly there is Range and CountableRange for half-open ranges.
For more information, see Range Types in SE-0065 A New Model for Collections and Indices.
let a = 0
let b = 10
var arr = [ClosedRange<Int>]()
let myRange: ClosedRange = a...b
arr.append(myRange)
for each in arr {
for every in [Int](each.lowerBound..<each.upperBound) {
print(every)
}
}
and remove that extention.

String convert to Int and replace comma to Plus sign

Using Swift, I'm trying to take a list of numbers input in a text view in an app and create a sum of this list by extracting each number for a grade calculator. Also the amount of values put in by the user changes each time. An example is shown below:
String of: 98,99,97,96...
Trying to get: 98+99+97+96...
Please Help!
Thanks
Use components(separatedBy:) to break up the comma-separated string.
Use trimmingCharacters(in:) to remove spaces before and after each element
Use Int() to convert each element into an integer.
Use compactMap (previously called flatMap) to remove any items that couldn't be converted to Int.
Use reduce to sum up the array of Int.
let input = " 98 ,99 , 97, 96 "
let values = input.components(separatedBy: ",").compactMap { Int($0.trimmingCharacters(in: .whitespaces)) }
let sum = values.reduce(0, +)
print(sum) // 390
For Swift 3 and Swift 4.
Simple way: Hard coded. Only useful if you know the exact amount of integers coming up, wanting to get calculated and printed/used further on.
let string98: String = "98"
let string99: String = "99"
let string100: String = "100"
let string101: String = "101"
let int98: Int = Int(string98)!
let int99: Int = Int(string99)!
let int100: Int = Int(string100)!
let int101: Int = Int(string101)!
// optional chaining (if or guard) instead of "!" recommended. therefore option b is better
let finalInt: Int = int98 + int99 + int100 + int101
print(finalInt) // prints Optional(398) (optional)
Fancy way as a function: Generic way. Here you can put as many strings in as you need in the end. You could, for example, gather all the strings first and then use the array to have them calculated.
func getCalculatedIntegerFrom(strings: [String]) -> Int {
var result = Int()
for element in strings {
guard let int = Int(element) else {
break // or return nil
// break instead of return, returns Integer of all
// the values it was able to turn into Integer
// so even if there is a String f.e. "123S", it would
// still return an Integer instead of nil
// if you want to use return, you have to set "-> Int?" as optional
}
result = result + int
}
return result
}
let arrayOfStrings = ["98", "99", "100", "101"]
let result = getCalculatedIntegerFrom(strings: arrayOfStrings)
print(result) // prints 398 (non-optional)
let myString = "556"
let myInt = Int(myString)