【问题标题】:How to create a dictionary by first letter of title but ignore articles like "the" or "a/an" etc如何按标题的第一个字母创建字典,但忽略“the”或“a/an”等文章
【发布时间】:2017-11-23 12:14:51
【问题描述】:

我想按表格视图中媒体项的标题对数据进行排序,但我想按第一个“实际”字母对数据进行排序,而不是像“the”或“an”这样的常见文章

  • 民谣
  • 红胡子海贼之歌
  • 黑胡子海贼之歌
  • 钢球

我尝试了以下解决方案:

  extension String {

    func firstLetter() -> Character{
            var tmp = self.lowercased()
            if tmp.hasPrefix("the "){
                tmp = String(tmp.characters.dropFirst(4))
            }else if tmp.hasPrefix("a "){
                tmp = String(tmp.characters.dropFirst(2))
            }else if tmp.hasPrefix("an "){
                tmp = String(tmp.characters.dropFirst(3))
            }
            let hmm = "aąbcćdeęfghijklmnoópqrsśtuvwxyzżź0123456789"
            let letters = Array(hmm.characters)
            for index in characters.indices{
                if letters.contains(tmp[index]){
                    return tmp[index]
                }
            }
            return "_"
        }

        func firstSpecial() -> Bool {
            let characterset = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
            if prefix(1).rangeOfCharacter(from: characterset.inverted) != nil {
                return true
            }else{
                return false
            }
        }

        func firstNumber() -> Bool {
            if lowercased().hasPrefix("the "){
                return Int(dropFirst(4).prefix(1)) != nil
            }else if lowercased().hasPrefix("a "){
                return Int(dropFirst(2).prefix(1)) != nil
            }else if lowercased().hasPrefix("an "){
                return Int(dropFirst(3).prefix(1)) != nil
            }else{
                return Int(prefix(1)) != nil
            }
        }
    }

songs = [MPMediaItem]()
result = [String: [MPMediaItem]]()
indexes = [String]()

    func setup(){
            var numbers = false
            var special = false
            songs = musicQuery.shared.songs
            for song in songs {
                var key = ""
                if song.title!.firstNumber() {
                    print(song.title)
                    key = "#"
                    if result[key] != nil {
                        result[key]?.append(song)
                    }else{
                        result[key] = []
                        result[key]?.append(song)
                        numbers = true
                    }
                }else if !(song.title?.firstSpecial())! {
                    key = String(describing: song.title?.firstLetter()).uppercased()
                    if result[key] != nil {
                        result[key]?.append(song)
                    }else{
                        result.updateValue([song], forKey: key)
                        indexes.append(key)
                    }
                }else{
                    print(song.title)
                    key = "?"
                    if result[key] != nil {
                        result[key]?.append(song)
                    }else{
                        result[key] = []
                        result[key]?.append(song)
                        special = true
                    }
                }
            }
            indexes = indexes.sorted { $0.compare($1) == ComparisonResult.orderedAscending }
            if numbers {
                indexes.append("#")
            }
            if special {
                indexes.append("?")
            }
        }

但这远非最佳,需要很长时间才能完成并且省略了一些条目

【问题讨论】:

    标签: ios swift string


    【解决方案1】:

    简化的解决方案,只需替换那些您在排序时不想包含的文章。

        let unsortedList = ["Ballad of El Red Beard Pirate","A Ballad","Ballad of An Red Beard Pirate","The Ballad of Black Beard Pirate","Balls of steel"];
    
        func removeLeadingArticle(string: String) -> String {
            let articles = ["The ", "A ", "of "," An"];
            var changedStr = string;
            for (_,article) in articles.enumerated() {
                changedStr = changedStr.replacingOccurrences(of: article, with: "");
            }
            print("changed string \(changedStr)");
            return changedStr;
        }
    
        let sortedList = unsortedList.sorted { (firstStr, secondStr) -> Bool in
            let title1 = removeLeadingArticle(string: firstStr);
            let title2 = removeLeadingArticle(string: secondStr);
            return title1.localizedCaseInsensitiveCompare(title2) == ComparisonResult.orderedAscending
        }
    
        print("sorted list \(sortedList)");
    

    输出------------

    changed string Ballad
    changed string Ballad El Red Beard Pirate
    changed string Ballad Red Beard Pirate
    changed string Ballad El Red Beard Pirate
    changed string Ballad Black Beard Pirate
    changed string Ballad Red Beard Pirate
    changed string Ballad Black Beard Pirate
    changed string Ballad El Red Beard Pirate
    changed string Ballad Black Beard Pirate
    changed string Ballad
    changed string Balls steel
    changed string Ballad Red Beard Pirate
    sorted list ["A Ballad", "The Ballad of Black Beard Pirate", "Ballad of El Red Beard Pirate", "Ballad of An Red Beard Pirate", "Balls of steel"]
    

    忽略“A”,“An”,“The”,获取以前导字符为键的字典。

        let unsortedList = ["A Ballad","Ballad of An Red Beard Pirate","The Ballad of Black Beard Pirate","All Balls of steel","Red Riding Hood","The Earth"];
    
        let articles = ["The","A","An"];
    
        var dictionary:Dictionary = Dictionary<String,String>();
    
        for objStr in unsortedList {
    
            let article = objStr.components(separatedBy: " ").first!;
    
            print("article: \(article)");
    
            if articles.contains(article) {
    
                if objStr.components(separatedBy: " ").count > 1 {
                    let secondStr = objStr.components(separatedBy: " ")[1];
                    dictionary["\(secondStr.first!)"] = objStr;
                }
            }else {
                dictionary["\(article.first!)"] = objStr;
            }
        }
    
    
        print("dictionary:- \(dictionary)");
    

    输出--------

    文章:A 文章:民谣 文章: 文章:全部 文章:红色 文章:

    dictionary:- ["R": "Red Riding Hood", "B": "The Ballad of Black Beard Pirate", "A": "All Balls of steel", "E": "The Earth"]
    

    【讨论】:

    • 您的删除前导文章功能会删除所有文章,包括字符串中间的文章,并导致排序错误。您不能只删除主要的所有文章。
    • 嗨@JoshHomann,作为问题,它清楚地表明他想通过忽略“an”、“the”、“a”等常见文章来进行排序。所以我这样做了。如果他想忽略第一篇文章,那么他应该使用你的答案,否则我的,我们在这里为他提供选项。
    【解决方案2】:

    为了做到这一点,您可以尝试使用这个实用函数来删除作为参数传递的任何前缀。它使用第一个匹配替换:https://stackoverflow.com/a/40863622/8236481 e

    extension String {
      func removingPrefixes(_ prefixes: [String]) -> String {
        var resultString = self
        prefixes.map {
          if resultString.hasPrefix($0) {
            resultString = resultString.dropFirst($0.count).description
          }
        }
        return resultString
      }
    }
    

    使用此函数,您现在可以使用此函数对字符串数组进行排序:

    extension Array where Element == String {
      func sorted(ignoring: [String]) -> [String] {
        let filteredData = self.map { $0.lowercased().removingPrefixes(ignoring) }
        let sortedData = filteredData.enumerated().sorted { $0.element < $1.element }
        return sortedData.map { self[$0.offset] }
      }
    }
    
    1. 从输入数据中删除所有不需要的文章。
    2. 对该数组进行排序
    3. 使用排序后的数组索引返回原始数组

    希望对你有所帮助!

    【讨论】:

    • stringByRemovingAll 不适用于这种情况;您需要删除主要文章,而不是字符串中的每篇文章。字符串中间的那些仍然有效,这会导致您的函数在某些情况下排序不正确。
    • 是的,只是编辑了我的答案,如果它是前缀则替换第一个出现。
    • 还是错了。您不应该替换“美女与野兽”中的第一次出现。您应该只替换“了不起的盖茨比”中的前缀。
    • 如果它有文章作为前缀,我只会替换第一个匹配项
    【解决方案3】:

    您可以使用数组的内置排序(变异)或排序(复制)函数按您希望的任何顺序进行排序。在这种情况下,您需要做的就是在比较两个字符串之前去掉任何您不想要的前缀。这不是最有效的解决方案,因为您可以缓存替换计算(即向您的模型添加一个具有预先计算的排序字符串的字段,或者使用预先计算的排序字符串创建一个字典),但它确实有效。

    import PlaygroundSupport
    import UIKit
    
    func strippingArticles(from: String) ->  String {
        let articles = ["The ", "A "]
        var target = from
        for article in articles {
           if target.hasPrefix(article) {
               target = String(target.dropFirst(article.count))
           }
        }
        return target
    }
    let unsortedTitle = ["Title", "The sample", "A word"]
    let sortedTitles = unsortedTitle.sorted { (lhs: String, rhs: String) -> Bool in
        let left = strippingArticles(from: lhs)
        let right = strippingArticles(from: rhs)
        return left.compare(right) == .orderedAscending
    }
    print(sortedTitles)
    

    【讨论】:

    • 嗨@joshhomann,为什么你删除了你的第一个答案,你可以选择编辑它。你可以编辑你的第一个答案而不是写另一个答案。
    【解决方案4】:

    不要试图弄清楚文章是什么;使用 iOS 已经内置的智能。使用NSLinguisticTagger 将文本标记为单词,从而将其分解为parts of speech。现在您知道在排序时要忽略哪些初始文本。

    之后,这只是一个协同排序的问题。而这在 Stack Overflow 上已经处理过很多次了。

    【讨论】:

      猜你喜欢
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-27
      • 2023-04-01
      • 2012-06-11
      • 1970-01-01
      相关资源
      最近更新 更多