草庐IT

swift - 在 Swift 中按类型和属性对异构集合进行排序

coder 2023-09-12 原文

我有一个不同类型的异构集合,它们都符合相同的协议(protocol)。我想按类型对数组进行排序,然后按名称属性对数组进行排序。例如,我希望这个动物数组按以下顺序按类型排序:狗、鸟,然后是鱼,如果它们属于同一类型,我想按名称排序。这是代码:

import Foundation

protocol Animal {
    var name: String { get set }
}

class Dog: Animal {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Bird: Animal {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Fish: Animal {
    var name: String

    init(name: String) {
        self.name = name
    }
}

let dogA = Dog(name: "A")
let dogB = Dog(name: "B")
let birdA = Bird(name: "A")
let birdB = Bird(name: "B")
let fishA = Fish(name: "A")
let fishB = Fish(name: "B")

let animals: [Animal] = [fishB, fishA, birdB, birdA, dogB, dogA]

let sortedAnimals = animals.sorted { first, second -> Bool in
    if first is Dog && !(second is Dog) {
        return true
    } else if first is Dog && second is Dog {
        return first.name < second.name
    }

    if first is Bird && !(second is Bird) {
        return true
    } else if first is Bird && second is Bird {
        return first.name < second.name
    }

    if first is Fish && !(second is Fish) {
        return true
    } else if first is Fish && second is Fish {
        return first.name < second.name
    }

    return first.name < second.name
}

sortedAnimals

这有效,并产生正确的排序顺序:

{name "A", type "Dog"}
{name "B", type "Dog"}
{name "A", type "Bird"}
{name "B", type "Bird"}
{name "A", type "Fish"}
{name "B", type "Fish"}

但由于在生产代码中我有超过 30 种不同的类型在集合中,这种重复感觉非常重复。如果没有那么多重复代码,我如何才能完成这种排序?

最佳答案

使用 [Animal.Type] 建立排序,然后先比较类型是否相等,以决定是否需要按 name 排序>类型

let order: [Animal.Type] = [Dog.self, Bird.self, Fish.self]

let sortedAnimals = animals.sorted { first, second -> Bool in
    let firstIndex = order.firstIndex { $0 == type(of: first) } ?? Int.max
    let secondIndex = order.firstIndex { $0 == type(of: second) } ?? Int.max

    if firstIndex == secondIndex {
        return first.name < second.name
    } else {
        return firstIndex < secondIndex
    }
}

注意事项:

  1. 如所写,缺失的类型将按 name 排序到数组末尾。
  2. 您可能想要添加:

    assert(firstIndex != Int.max, "missing type \(type(of: first)) from order array")
    assert(secondIndex != Int.max, "missing type \(type(of: second)) from order array")
    

    捕捉 order 数组中缺失的类型。虽然您可以强制展开 firstIndex(where:) 的结果,但 assert 提供了在 Debug 构建中查找缺失类型的能力,但是在 Release 版本中消失。

  3. 元组比较(如@Hamish 用 this answer 解释的那样)可用于将上面的 if 语句替换为:

    return (firstIndex, first.name) < (secondIndex, second.name)
    

    感谢@MartinR 的提醒!

关于swift - 在 Swift 中按类型和属性对异构集合进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54598618/

有关swift - 在 Swift 中按类型和属性对异构集合进行排序的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  5. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  6. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  7. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  8. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  9. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  10. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

随机推荐