Swift:通过多个条件对嵌套数组进行分组-改进算法

我有一个具有多个属性的对象数组。我需要通过这些属性将其分组。我已经写了可以做到这一点的算法。但是,我希望有一个更简洁和可重用的工具,以便我可以以其他方式对项目进行分组。

给出一个对象数组:

@objcMembers class Object: NSObject {
    let name: UUID = UUID()
    let value1: Int = Int(arc4random_uniform(6) + 1)
    let value2: Int = Int(arc4random_uniform(6) + 1)
    let value3: Int = Int(arc4random_uniform(6) + 1)
    static func == (lhs: Object,rhs: Object) -> Bool {
        lhs.name == rhs.name
    }
}
[
    Object1 {4,4,1},Object2 {1,3,2},...
    Object99 {3,]

...并给出两个数据结构,GroupSection

struct Group {
    let title: String?
    let sections: [Section]
}

struct Section {
    let title: String?
    let items: [Object]
}

我需要得到以下结果:

Value1: 1 // Group
    Value2: 1 - Value3: 1 // Section
        Object1
        Object2
        Object3
    Value2: 1 - Value3: 2 // Section
        Object1
    Value2: 2 - Value3: 1 // Section
        Object1
        Object2
        Object3
Value1: 2 // Group
    Value2: 1 - Value3: 5 // Section
        Object1
    Value2: 4 - Value3: 1 // Section
        Object1
    Value2: 4 - Value3: 2 // Section
        Object1
        Object2
        Object3

因此,对象按照它们的sectionsValue3分组为Value2并按升序排序。

然后,这些部分按其groups分组为Value1,并再次以升序排序。

我当前的算法以一种基本的命令性方法实现,我相信有很多地方有待改进。

我已经尝试过使用Swift的Dictionary.init(grouping:by:)初始化程序,然后使用Dictionary.mapValues方法对条目进行进一步分组。但是,Swift的字典没有排序,因此我必须再次进行深入的排序。

当前,我的算法如下:

        // Sort the array
        let value1BasedDescriptors = [
            NSSortDescriptor(keyPath: \Object.value1,ascending: true),NSSortDescriptor(keyPath: \Object.value2,NSSortDescriptor(keyPath: \Object.value3,]
        let sorted = (Array(objects) as NSArray).sortedArray(using: value1BasedDescriptors) as! [Object]

        // Keep the previous object to find when one of the properties change
        var previousObject: Object?
        // Keep the group to be filled with sections
        var currentGroup = [Section]()
        // Keep the section to be filled with objects
        var currentSection = [Object]()
        // All the groups to be returned by the function
        var groups = [Group]()

        // Iterate over each object
        for object in sorted {
            // If it's a first time in a loop,set a previous object and skip
            if previousObject == nil {
                previousObject = object
                // Append to the current section
                currentSection.append(object)
                continue
            }
            // If one of the value3 or value2 is different from the previously visited object -> Create a new section with the appropriate title
            if object.value3 != previousObject?.value3 || object.value2 != previousObject?.value2 {
                let section = Section(title: "Value2: \(previousObject?.value2) - Value3: \(previousObject?.value3)",items: currentSection)
                // Add it to current group
                currentGroup.append(section)
                // Empty the section
                currentSection.removeAll()
            }

            // If Value1 is different,group all the objects into group
            if object.value1 != previousObject?.value1 {
                let group = Group(title: "Value1: \(previousObject?.value1)",sections: currentGroup)
                groups.append(group)
                currentGroup.removeAll()
            }

            // Always add a visited object to a current section
            currentSection.append(object)

            // And mark as previous
            previousObject = object
        }

        // since the last group & section won't be added in a loop,we have to add them manually
        let section = Section(title: "Value2: \(previousObject?.value2) - Value3: \(previousObject?.value3)",items: currentSection)
        currentGroup.append(section)
        let group = Group(title: "Value1: \(previousObject?.value1)",sections: currentGroup)
        groups.append(group)


        debugPrint(groups)

它确实实现了我需要实现的目标,但是,这里有一些局限性:

  1. 如果要按以下顺序对对象进行分组:Value2-> Value1-> Value3?还是其他命令?然后,我必须编写相同的算法,但要更改属性
  2. 如果我必须多次编写相同的算法,如何将其缩短,例如使用功能性还是面向对象的方法?

完整的代码清单(复制粘贴到Playground或AppDelegate.swift文件中):


        struct Group {
            let title: String?
            let sections: [Section]
        }

        struct Section {
            let title: String?
            let items: [Object]
        }

        @objcMembers class Object: NSObject {
            let name: UUID = UUID()
            let value1: Int = Int(arc4random_uniform(6) + 1)
            let value2: Int = Int(arc4random_uniform(6) + 1)
            let value3: Int = Int(arc4random_uniform(6) + 1)
            static func == (lhs: Object,rhs: Object) -> Bool {
                lhs.name == rhs.name
            }
        }

        // Create a lot of objects
        var objects = Set<Object>()
        for i in 0...100 {
            objects.insert(Object())
        }

        // Sort the array
        let value1BasedDescriptors = [
            NSSortDescriptor(keyPath: \Object.value1,sections: currentGroup)
        groups.append(group)


        debugPrint(groups)

arielyangjing520 回答:Swift:通过多个条件对嵌套数组进行分组-改进算法

这就是我要怎么做。我将使用Dictionary(_:groupingBy:)产生一个组,然后将该字典作为映射过程的输入,将key:value对转换为Group对象。映射本身涉及另一个过程,即调用Dictionary(_:groupingBy:)value2进行分组,然后将这些key:value对映射到Section对象中。

要添加您要查找的自定义内容,您可以替换这些重复的Dictionary(_:groupingBy:)mapsorted调用的嵌套,可以通过递归替换为递归键路径(代表您希望各层分组所依据的值)

import Foundation

struct Object: Equatable {
//  let name: UUID = UUID()
    let value1 = Int.random(in: 1...6)
    let value2 = Int.random(in: 1...6)
    let value3 = Int.random(in: 1...6)

    static func == (lhs: Object,rhs: Object) -> Bool {
        return (lhs.value1,lhs.value2,lhs.value3) == (rhs.value1,rhs.value2,rhs.value3)
    }
}

extension Object: Comparable {
    static func < (lhs: Object,lhs.value3) < (rhs.value1,rhs.value3)
    }
}

struct Group: CustomDebugStringConvertible {
    let title: String
    let sections: [Section]

    var debugDescription: String {
        let sectionText = self.sections
            .map { "\t" + $0.debugDescription }
            .joined(separator: "\n")

        return "Group: \(self.title)\n\(sectionText)"
    }
}

struct Section: CustomDebugStringConvertible {
    let title: String
    let items: [Object]

    var debugDescription: String {
        let itemText = self.items
            .map { "\t\t" + String(describing: $0) }
            .joined(separator: "\n")

        return "Section: \(self.title)\n\(itemText)"
    }
}

let input = (0...100).map { _ in Object() }.sorted()

let groups = Dictionary(grouping: input,by: { $0.value1 })
    .map { (arg: (key: Int,rawSections: [Object])) -> Group in
        let (key,rawSections) = arg
        let sections = Dictionary(grouping: rawSections,by: { $0.value2 })
            .map { key,objects in
                Section(title: String(key),items: objects.sorted { $0.value3 < $1.value3 })
            }
            .sorted { $0.title < $1.title }
        return Group(title: String(key),sections: sections)
    }
    .sorted(by: { $0.title < $1.title })

for group in groups {
    debugPrint(group)
}
本文链接:https://www.f2er.com/3107449.html

大家都在问