Swift превращает код страны в флаг emoji через unicode
Я ищу быстрый способ сделать что-то вроде:
let germany = "DE"
в
let flag = "\u{1f1e9}\u{1f1ea}"
т.е. то, что отображение D
в 1f1e9
и E
до 1f1ea
Я смотрел на .utf8
для строки, но это возвращает целое число.
FWIW моей общей целью является возможность взять произвольный код страны и получить соответствующий флаг emoji.
EDIT: Мне тоже хорошо, просто удерживая таблицу, которая делает это сопоставление, если оно доступно где-то. Я googled вокруг, но не нашел его.
Ответы
Ответ 1
Здесь приведена общая формула для превращения двухбуквенного кода страны в свой флаг emoji:
func flag(country:String) -> String {
let base = 127397
var usv = String.UnicodeScalarView()
for i in country.utf16 {
usv.append(UnicodeScalar(base + Int(i)))
}
return String(usv)
}
let s = flag("DE")
РЕДАКТИРОВАТЬ Ooops, нет необходимости проходить через вложенную структуру String.UnicodeScalarView. Оказывается, что для этой цели String имеет метод append
. Итак:
func flag(country:String) -> String {
let base : UInt32 = 127397
var s = ""
for v in country.unicodeScalars {
s.append(UnicodeScalar(base + v.value))
}
return s
}
EDIT Oooops снова, в Swift 3 они убрали возможность добавления UnicodeScalar к String, и они сделали инициализатор UnicodeScalar неудачным (Xcode 8 seed 6), так что теперь это выглядит так:
func flag(country:String) -> String {
let base : UInt32 = 127397
var s = ""
for v in country.unicodeScalars {
s.unicodeScalars.append(UnicodeScalar(base + v.value)!)
}
return String(s)
}
Ответ 2
Если кто-то ищет решение в ObjectiveC здесь, это удобная категория:
@interface NSLocale (RREmoji)
+ (NSString *)emojiFlagForISOCountryCode:(NSString *)countryCode;
@end
@implementation NSLocale (RREmoji)
+ (NSString *)emojiFlagForISOCountryCode:(NSString *)countryCode {
NSAssert(countryCode.length == 2, @"Expecting ISO country code");
int base = 127462 -65;
wchar_t bytes[2] = {
base +[countryCode characterAtIndex:0],
base +[countryCode characterAtIndex:1]
};
return [[NSString alloc] initWithBytes:bytes
length:countryCode.length *sizeof(wchar_t)
encoding:NSUTF32LittleEndianStringEncoding];
}
@end
Тест:
for ( NSString *countryCode in [NSLocale ISOCountryCodes] ) {
NSLog(@"%@ - %@", [NSLocale emojiFlagForISOCountryCode:countryCode], countryCode);
}
Выход:
🇦🇩 - AD
🇦🇪 - AE
🇦🇫 - AF
🇦🇬 - AG
🇦🇮 - AI
...
Ответ 3
Чтобы лучше понять матовый ответ
версия Swift 2
public static func flag(countryCode: String) -> Character {
let base = UnicodeScalar("🇦").value - UnicodeScalar("A").value
let string = countryCode.uppercaseString.unicodeScalars.reduce("") {
var string = $0
string.append(UnicodeScalar(base + $1.value))
return string
}
return Character(string)
}
версия Swift 3, взятая из https://github.com/onmyway133/Smile/blob/master/Sources/Smile.swift#L52
public func emoji(countryCode: String) -> Character {
let base = UnicodeScalar("🇦").value - UnicodeScalar("A").value
var string = ""
countryCode.uppercased().unicodeScalars.forEach {
if let scala = UnicodeScalar(base + $0.value) {
string.append(String(describing: scala))
}
}
return Character(string)
}
Ответ 4
Как обычно @matt имеет правильный ответ.
Его решение для Swift3 с использованием flatMap
и joined
:
func flag(country: String) -> String {
let base: UInt32 = 127397
return country.unicodeScalars.flatMap { String.init(UnicodeScalar(base + $0.value)!) }.joined()
}
Что flatMap
делает: он отображает каждый unicodeScalar
of country
вместе с базой в новый String
, который является joined
к результату.
Ответ 5
Две оптимизации матового ответа.
- Не нужно проходить через вложенную строку в Swift 4
- Чтобы избежать строки нижнего регистра, я добавил uppercased()
Вот код.
func flag(from country:String) -> String {
let base : UInt32 = 127397
var s = ""
for v in country.uppercased().unicodeScalars {
s.unicodeScalars.append(UnicodeScalar(base + v.value)!)
}
return s
}
Ответ 6
Для более функционального подхода, используя не изменяемые переменные, используйте это:
private func flag(country: String) -> String {
let base: UInt32 = 127397
return country.unicodeScalars
.flatMap({ UnicodeScalar(base + $0.value) })
|> String.UnicodeScalarView.init
|> String.init
}
Где оператор |>
- оператор приложения функции, работающий как "труба" для более естественного порядка чтения: мы берем скаляры, сопоставляем их в новые скаляры, превращаем их в представление, а это в строку.
Он определяется так:
infix operator |> : MultiplicationPrecedence
func |> <T, U>(left: T, right: (T) -> U) -> U {
return right(left)
}
Без пользовательских операторов мы все равно можем обойтись без изменяемого состояния:
private func flag(country: String) -> String {
let base: UInt32 = 127397
return String(String.UnicodeScalarView(
country.unicodeScalars.flatMap({ UnicodeScalar(base + $0.value) })
))
}
Но IMHO, это читает немного "назад", так как естественный поток операций не читает ни входы, ни входы, но немного их обоих.