Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
291 views
in Technique[技术] by (71.8m points)

swift - Sort array of custom objects depending on dynamic criterias

Say we have the following Object, Priority & Requirement objects:

enum Priority: CaseIterable {
    case priorityA = "priority_a"
    case priorityB = "priority_b"
    case priorityC = "priority_c"
    case priorityD = "priority_d"
    case priorityE = "priority_e"
    case priorityF = "priority_f"
    case priorityG = "priority_g"
    ... 

    var weight: Int {
        switch self {
            ...
        }
    }
}

struct Object {
    let name: String
    let priorities: [Priority: Int]
}

enum RequirementBaseValue {
    case any
    case minimum(Int)
    case maximum(Int)
}

struct Requirement {
    let priority: Priority
    let baseValue: RequirementBaseValue
}

An example:

let requirementA = Requirement(priority: .priorityB, baseValue: .any)
let requirementB = Requirement(priority: .priorityC, baseValue: .minimum(100))
let requirements = [requirementA, requirementB]

let aPriorities =  [..., .priorityB: 0, .priorityC: 0, ... ]
let objectA = Object(name: "object_a", priorities: aPriorities)

let bPriorities = [..., .priorityB: 950, .priorityC: 210, ... ]
let objectB = Object(name: "object_b", priorities: bPriorities)

let cPriorities = [..., .priorityB: 950, .priorityC: 300, ... ]
let objectC = Object(name: "object_c", priorities: cPriorities)

let dPriorities = [..., .priorityB: 250, .priorityC: 101,  ... ]
let objectD = Object(name: "object_d", priorities: dPriorities)

let objects = [objectA, objectB, objectC, objectD]

The goal is to sort these objects:

let sortedObjects = objects.sorted(by: { ... })

For example, considering the weights of each priority is equal to 1, for the example above we would have:

print(sortedObjects)
// [objectC, objectB, objectD, objectA]

And a few details about how the sorting should be performed:

  • If the requirement baseValue is set to any: the higher the resultValue the earlier it should appear in the sorted array.
  • If the requirement value is either set to minimum(Int) or maximum(Int): if the priority doesn't match the condition, the object should be filtered out from the array, otherwise the higher the resultValue the earlier it should appear in the sorted array.
  • If an object's priorities dictionary doesn't contain a specific priority, we consider the baseValue for this priority to be equal to 0.
  • The resultValue is obtained by multipying the weight property of a priority by the baseValue of the priority.
  • Each object will usually have between 80 to 200 priorities (maximum will be n=Priority.allCases.count).
  • The objects array will usually have between 3000 to 5000 elements so performance will be important.

I was thinking of doing something like this:

let sortedObjects = objects.sorted(by: { object in
   for requirement in requirements {
       let baseValue = object.priorities[requirement.priority] ?? 0
       let weight = requirement.priority.weight
       let resultValue = baseValue * weight
       ...
   } 
   return ...
}

But then I'm not sure how to return the boolean because there are multiple requirements & priorities for each object, and it's not just an AND or OR condition, but more of an else-if situation.

Thank you for your help!

question from:https://stackoverflow.com/questions/65947568/sort-array-of-custom-objects-depending-on-dynamic-criterias

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

"...if the priority doesn't match the condition, the object should be filtered out from the array"

Looks like you're mixing two operations into one. The more straightforward way is to filter the array first and then sort it.

Something like this:

func isSatisfying(requirement: Requirement, object: Object) -> Bool {
    let value = object.priorities[requirement.priority] ?? 0
    switch requirement {
    case .any: return true
    case .minimum(let minValue): return value >= minValue
    case .maximum(let maxValue): return value <= maxValue
}

let filteredObjects = objects.filter { obj in
    // returns true if all requirements are satisfied
    return requirements.allSatisfy { req in
        return isSatisfying(requirement: req, object: obj)
    }
}
let sortedObjects = filteredObjects.sort { first, second in
    let firstResultValue = ...
    let secondResultValue = ...
    return firstResultValue >= secondResultValue
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...