Vyacheslav
Vyacheslav

Reputation: 27211

Get a value at index from range

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?

Upvotes: 9

Views: 790

Answers (2)

Hamish
Hamish

Reputation: 80811

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

Upvotes: 8

Yury
Yury

Reputation: 6114

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

Upvotes: 4

Related Questions