Подтвердить что ты не робот

Swift превращает код страны в флаг emoji через unicode

Я ищу быстрый способ сделать что-то вроде:

let germany = "DE" 

в

let flag = "\u{1f1e9}\u{1f1ea}"

т.е. то, что отображение D в 1f1e9 и E до 1f1ea Я смотрел на .utf8 для строки, но это возвращает целое число.

FWIW моей общей целью является возможность взять произвольный код страны и получить соответствующий флаг emoji.

EDIT: Мне тоже хорошо, просто удерживая таблицу, которая делает это сопоставление, если оно доступно где-то. Я googled вокруг, но не нашел его.

4b9b3361

Ответ 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, это читает немного "назад", так как естественный поток операций не читает ни входы, ни входы, но немного их обоих.