如何通过第一个标题字母创建字典,但忽略“the”或“a / an”等文章

时间:2017-11-23 12:14:51

标签: ios swift string

我想按照表格视图中的媒体项目标题对数据进行排序,但我希望按照第一个“实际”字母对其进行排序,而不使用“the”或“an”之类的常见文章

  • A Ballad
  • 红胡子海盗之歌
  • 黑胡子海盗之歌
  • 钢球

我尝试了以下解决方案:

  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("?")
            }
        }

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

4 个答案:

答案 0 :(得分: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"]

答案 1 :(得分:1)

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

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. 使用排序数组索引返回原始数组
  4. 希望它能帮到你!

答案 2 :(得分:0)

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

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)

答案 3 :(得分:0)

不要试图找出文章的内容;使用已经内置到iOS的智能。使用NSLinguisticTagger将文字标记为单词,然后将其分解为parts of speech。现在您知道在排序时要忽略的初始文本。

在那之后,这只是一个cosorting问题。这就是Stack Overflow已经多次处理过的事情。