Swift - лучшая практика для поиска самой длинной строки в массиве [String]

Я пытаюсь найти наиболее эффективный способ получить самую длинную строку в массиве строк. Например:

let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]

и результат будет - "Game Of Thrones is just good"

Я пытался использовать maxElement func, хотя это дает максимальную строку в буквенных идеях (maxElement()).

Какие-либо предложения? Спасибо!

5 ответов

Решение

Вместо сортировки, которая является O(n log (n)) для хорошей сортировки, используйте max(by:) значение O(n) в массиве, обеспечивающее замыкание для сравнения длин строк:

Свифт 4:

Для Swift 4 вы можете получить длину строки с помощью count собственность на String:

let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]

if let max = array.max(by: {$1.count > $0.count}) {
    print(max)
}

Свифт 3:

использование .characters.count на String чтобы получить длину строки:

let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]

if let max = array.max(by: {$1.characters.count > $0.characters.count}) {
    print(max)
}

Свифт 2:

использование maxElement на массиве, обеспечивающем его замыкание для сравнения длин строк:

let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]

if let max = array.maxElement({$1.characters.count > $0.characters.count}) {
    print(max)
}

Замечания: maxElement является O(n). Хорошей сортировкой является O(n log (n)), поэтому для больших массивов это будет намного быстрее, чем сортировка.

Ты можешь использовать reduce сделать это. Он будет перебирать ваш массив, отслеживая текущую самую длинную строку, а затем вернет ее после завершения.

Например:

let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]

if let longestString = array.reduce(Optional<String>.None, combine:{$0?.characters.count > $1.characters.count ? $0:$1}) {
    print(longestString) // "Game Of Thrones is just good"
}

(Обратите внимание, что Optional.None сейчас Optional.none в Свифт 3)

Это использует nil начальное значение для учета того факта, что массив может быть пустым, как указано @JHZ (он вернет nil в таком случае). Если вы знаете, что в вашем массиве есть хотя бы один элемент, вы можете упростить его до:

let longestString = array.reduce("") {$0.characters.count > $1.characters.count ? $0:$1}

Поскольку он проходит через каждый элемент только один раз, он будет быстрее, чем при использовании sort(), Я сделал быстрый тест и sort() появляется примерно в 20 раз медленнее (хотя нет смысла в преждевременной оптимизации, я чувствую, что стоит упомянуть).


Редактировать: я рекомендую вам использовать решение @vacawama, поскольку оно даже чище, чем reduce!

Ну вот:

let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]

var sortedArr = array.sort() { $0.characters.count > $1.characters.count }

let longestEelement = sortedArr[0]

Вы также можете попрактиковаться в использовании Generics, создав эту функцию:

      func longestString<T:Sequence>(from stringsArray: T) -> String where T.Iterator.Element == String{
    return (stringsArray.max {$0.count < $1.count}) ?? ""
}

Объяснение: Создайте функцию с именем longestString. Объявите, что существует универсальный тип T, который реализует протокол Sequence (Sequence определяется здесь: https://developer.apple.com/documentation/swift/sequence ). Функция вернет одну строку (конечно, самую длинную). Предложение where объясняет, что общий тип T должен быть ограничен элементами типа String.

Внутри функции вызовите функцию max массива stringsArray, сравнив самую длинную строку элементов внутри. Будет возвращена самая длинная строка (необязательно, так как она может быть нулевой, если массив пуст). Если самая длинная строка равна нулю, то (использование ??) вместо этого возвращает пустую строку как самую длинную строку.

Теперь назовите это:

      let longestA = longestString(from:["Shekinah", "Chesedh", "Agape Sophia"])

Если вы научились использовать дженерики, даже если строки скрыты внутри объектов, вы можете использовать шаблон кодирования, описанный выше. Вы можете изменить элемент на объекты того же класса (например, Person).

Таким образом:

      class Person {
    let name: String
    init(name: String){
        self.name = name
    }
}

func longestName<T:Sequence>(from stringsArray: T) -> String where T.Iterator.Element == Person{
    return (stringsArray.max {$0.name.count < $1.name.count})?.name ?? ""
}

Затем вызовите функцию следующим образом:

      let longestB = longestName(from:[Person(name: "Shekinah"), Person(name: "Chesedh"), Person(name: "Agape Sophia")])

Вы также можете переименовать свою функцию в зависимости от целесообразности ее использования. Вы можете настроить шаблон так, чтобы он возвращал что-то еще, например, сам объект или длину (количество) строки. И, наконец, знакомство с дженериками может улучшить ваши навыки кодирования.

Теперь, снова немного подправив, вы можете расширить возможности, чтобы сравнивать строки, принадлежащие многим разным типам, если они реализуют общий протокол.

      protocol Nameable {
    var name: String {get}
}

Это определяет протокол с именем Nameable, который требует, чтобы те, кто его реализует, имели переменную имени типа String. Далее мы определяем две разные вещи, обе из которых реализуют протокол.

      class Person: Nameable {
    let name: String
    init(name: String){
        self.name = name
    }
}

struct Pet: Nameable {
    let name: String
}

Затем мы настраиваем нашу универсальную функцию так, чтобы она требовала, чтобы элементы соответствовали Nameable, несмотря на то, что они сильно различаются.

      func longestName<T:Sequence>(from stringsArray: T) -> String where T.Iterator.Element == Nameable{
return (stringsArray.max {$0.name.count < $1.name.count})?.name ?? ""
}

Давайте соберем разные объекты в массив. Затем вызовите нашу функцию.

      let myFriends: [Nameable] = [Pet(name: "Bailey"), Person(name: "Agape Sophia")]

let longestC = longestName(from: myFriends)

Наконец, зная «где» выше и «последовательность» выше, вы можете просто расширить последовательность:

      extension Sequence where Iterator.Element == String {

    func topString() -> String {
        self.max(by: { $0.count < $1.count }) ?? ""
    }
}

Или тип протокола:

      extension Sequence where Iterator.Element == Nameable {
    func theLongestName() -> Nameable? {
        self.max(by: { $0.name.count < $1.name.count })
    }
}

@vacawama — хороший вариант, но следует учитывать, что у вас может быть несколько элементов одинаковой длины. Таким образом, результатом «получить самую длинную строку в массиве строк» ​​может быть несколько строк одинаковой длины. Если это так, это может сработать:

      if let max = array.max(by: {$1.count > $0.count}) {
    let largeStrings = array.filter{$0.count == max.count}
    print(largeStrings)
}
Другие вопросы по тегам