如何按字母顺序对二维数组中的数据进行排序?

3

我目前在一个二维数组中排序数据时遇到了大问题。接下来我会尽可能详细地向您描述情况。

目前,我正在使用CNContactStore获取我的联系人信息。这一切都很好用。我能够检索出我想要的所有联系人数据。

现在,我创建了以下结构体:

struct FavoritableContact {
    let contact: CNContact
    var hasFavorited: Bool
}

我声明并初始化了以下数组:

var favoritableContacts = [FavoritableContact]()

当我检索到我的联系人后,我只需将它们添加到favoritableContacts中即可。

try store.enumerateContacts(with: request, usingBlock: { (contact, stopPointerIfYouWantToStopEnumerating) in

  favoritableContacts.append(FavoritableContact(contact: contact, hasFavorited: false))

})

为了将它们按字母顺序排序到同一个数组中,我只需要执行以下操作:
 var sortedContacts = favoritableContacts.sorted { $0.contact.familyName < $1.contact.familyName }

现在如果可能的话,我想创建以下的二维数组:
var 2D = [
        [FavoritableContact] //"A"
        [FavoritableContact], //"B"
        [FavoritableContact], //"C"
        [FavoritableContact], //"D"
        ...
    ]

我不确定如何将我的sortedContacts数组按字母顺序分开。

我在这里很新,如果我忘了什么或者做错了什么,请告诉我。


不要像上面创建的二维数组那样创建一个数组,似乎创建一个字典来保存按字母顺序排序过的联系人更有意义。因此,请创建一个字典,其中键是字母("a"、"b"、"c" 等),值是已按字母顺序排序的 FavoritableContact 数组。您可以通过循环遍历联系人并检查 familyName 的第一个字母来创建此字典。 - timgcarlson
这是我一直在尝试做的事情。但我期待着将联系人放入表格视图中。因此,我会使用节和行来迭代遍历二维数组,并在表格视图上显示它。 - user8169082
1
你仍然可以使用字典作为tableView的数据源。你可以简单地使用一个包含字母表的数组来保持字母顺序列表,在tableView方法中,你可以获取数组的计数来获取键"A",例如,以知道你有多少个"A"联系人等。 - creeperspeak
一个TableView的数据源不一定是一个数组,实际上,字典是一个相当常见的TableView集合的数据源。在你的情况下,我假设每个字母(A-Z)都代表一个分区,而每个数组都作为字典中值存储的行。如果键不存在,分区将为空。 - timgcarlson
4个回答

0
正如评论中指出的那样,以首字母作为键的字典可能是更好的选择,因为它更容易访问。不过,也许您有使用二维数组的原因。要实现这一点,您可以这样做:
//Create an empty array filled with 26 arrays of FavorableContact
var array2d = Array<[FavoritableContact]>(repeating: [FavoritableContact](), count: 26)

//Find the ascii value for "A" to use as your base
let aAscii = Int("A".unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])  //This returns 65, btw, so you could also just hardcode

//Go through your original array, find the first letter of each contact, and append to the correct array
favoritableContacts.forEach { (contact) in

    //Get the ascii value for the first letter
    let firstLetter = Int(contact.contact.familyName.prefix(1).uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])

    //Append to the array for this letter by subtracting the ascii value for "A" from the ascii value for the uppercased version of this letter.
    array2d[firstLetter - aAscii].append(contact)
}

这并不是世界上最干净的东西,它假设标准英语字母表中没有变音符号、符号、数字或其他任何东西。假设这是真的,它可以完成工作。


这就是了,伙计..非常感谢!不过,你能详细说明一下你如何处理aAscii吗?我不理解那行代码!谢谢! - user8169082
并不是每个人的名字都以拉丁字母表A-Z开头。 - Alexander
1
@JavierV。是的:A的ASCII值为65,B为66,以此类推。因此,由于我们需要将字母表翻译成字母表的数组索引,我们需要将A变为0,B变为1等等。因此,一旦我们知道A是65,我们就可以从每个字母的ASCII值中减去65来实现这一点。您也可以只创建一个更大的数组来容纳所有ASCII可能性,那么您就不必这样做,但我的代码只是一个示例。您可能需要更多地考虑这一点,因为其他字符可能会出现,但这将让您入门。 - creeperspeak
1
@Alexander 是的,我在我的答案中也提到了。这不是一个全面的解决方案 - 只是一个示例,告诉提问者如何入门。 - creeperspeak
@creeperspeak 太棒了!我现在能够检查 ASCII 表中的每个字符并检查它是否存在于键盘上。唯一的问题是我缺少其他语言和表情符号!我该怎么处理? - user8169082
这就是使用2D数组会让你受伤的地方。如果你需要使用非ASCII字符,比如表情符号和带有重音的字母等,你需要使用字典,因为这将允许你处理Unicode字符。查看其他答案以获取此类问题的简单解决方案。 - creeperspeak

0

可以使用类似这样的东西。

var contactsLeftToSort : [FavoritableContact] = []
var doubleArray : [[FavoritableContact]?] = [[FavoritableContact]?]()

var index : Int = 0

for char in "ABCDEFGHIJKLMNOPQRSTUV" {
    doubleArray.append(nil)
    var i = 0
    while i < contactsLeftToSort.count {
        let contact = contactsLeftToSort[i]
        if contact.name.first == char {
            doubleArray[index] == nil ? doubleArray[index] = [contact] : doubleArray[index]!.append(contact)
            contactsLeftToSort.remove(at: i)
        }

        //assuming original list is alphabetized.. if not, delete this line.
        if contact.name.first! > char { break }
        i += 1
    }
    index += 1
}

0
正如我在上面的评论中所写,我认为你可以通过使用字典而不是数组来更优雅地实现这一点。 SWIFT 4
let sortedContacts: [FavoritableContact] = ... // An array of FavoritableContact objects, they should be sorted
let groupedContacts = Dictionary(grouping: contacts, by { $0.familyName.first! })

现在你有了一个通讯录字典,其中键为字母表中的字母(即A-Z),值为已排序的FavoritableContact对象数组(假设在创建字典之前,你已经对FavoritableContacts的大数组进行了排序)。
如果你想将其用作tableview的数据源,你需要将部分数量设置为所有可能的姓氏首字母。对于每个部分中的行数,你可以返回该键的数组计数,如下所示:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    let letterForSection = letterForSection() // Custom method to get the section of the letter
    return contactsDict[letterForSection].count
}

数据源的其他方法的工作方式类似。


我正在跟随YouTube上的视频进行操作。我刚刚在我的代码中添加了一些内容,我将创建一个新的帖子!谢谢你提供的信息。我会在这里粘贴新的帖子。感谢你,我已经做了至少5个小时了...我是一名新程序员。干杯! - user8169082
已经有Dictionary(grouping:by:)可以完成你在那个循环中所做的事情。 - Alexander
@Alexander,我在你发布这个问题的同时更新了我的答案!今天早上刚学到了那个方法,非常有用。 - timgcarlson

0
兄弟,所有这些答案都过于复杂了。你只需要类似以下的东西:
let groupedContacts = Dictionary(grouping: contacts, by: { $0.contact.firstName.first! })


for initial, contacts in groupedContacts.lazy.sorted().{ $0.key < $1.key} {
    print("#################", initial)
    contacts.forEach{ print($0) }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接