Swift 3:与一组对象的group by的和值

时间:2016-11-17 14:15:48

标签: ios arrays swift

我在viewController中有这段代码

var myArray :Array<Data> = Array<Data>()
for i in 0..<mov.count {    
   myArray.append(Data(...))
}

class Data {
    var value :CGFloat
    var name  :String=""
    init({...})
}

我的数据输入为:

  • 10.5 apple
  • 20.0 lemon
  • 15.2 apple
  • 45

一旦我循环,我想返回一个新数组:

  • sum(value)group by name
  • 删除最后一行,因为没有名称
  • 按值排序

基于输入的预期结果:

  • 25.7 apple
  • 20.0 lemon
  • ,别无其他

我写了很多行代码而且发布它太混乱了。我找到了更简单的方法,有人对此有所了解吗?

3 个答案:

答案 0 :(得分:2)

首先,您不应该为您的班级Data命名,因为这是基础班级的名称。我使用了一个名为MyData的结构代替:

struct MyData {
    let value: CGFloat
    let name: String
}

let myArray: [MyData] = [MyData(value: 10.5, name: "apple"),
                         MyData(value: 20.0, name: "lemon"), 
                         MyData(value: 15.2, name: "apple"),
                         MyData(value: 45,   name: "")]

您可以使用字典来添加与每个名称关联的值:

var myDictionary = [String: CGFloat]()
for dataItem in myArray {
    if dataItem.name.isEmpty {
        // ignore entries with empty names
        continue
    } else if let currentValue = myDictionary[dataItem.name] {
        // we have seen this name before, add to its value
        myDictionary[dataItem.name] = currentValue + dataItem.value
    } else {
       // we haven't seen this name, add it to the dictionary
        myDictionary[dataItem.name] = dataItem.value
    }
}

然后你可以将字典转换回MyData个对象的数组,对它们进行排序并打印出来:

// turn the dictionary back into an array
var resultArray = myDictionary.map { MyData(value: $1, name: $0) }

// sort the array by value
resultArray.sort { $0.value < $1.value }

// print the sorted array
for dataItem in resultArray {
    print("\(dataItem.value) \(dataItem.name)")
}

答案 1 :(得分:2)

首先在Swift 3中保留Data,该示例使用名为Item struct

struct Item {
    let value : Float
    let name  : String
}

使用您给定的值

创建数据数组
let dataArray = [Item(value:10.5, name:"apple"), 
                 Item(value:20.0, name:"lemon"), 
                 Item(value:15.2, name:"apple"), 
                 Item(value:45, name:"")]

以及结果数组:

var resultArray = [Item]()

现在过滤所有非空的名称并生成Set - 每个名称在集合中出现一次:

let allKeys = Set<String>(dataArray.filter({!$0.name.isEmpty}).map{$0.name})

通过密钥迭代,使用相同名称过滤dataArray中的所有项目,汇总值并创建一个总值为Item的新值:

for key in allKeys {
    let sum = dataArray.filter({$0.name == key}).map({$0.value}).reduce(0, +)
    resultArray.append(Item(value:sum, name:key))
}

最后按值desscending对结果数组进行排序:

resultArray.sorted(by: {$0.value < $1.value})

答案 2 :(得分:0)

首先更改数据类,使字符串成为可选字符串,它变得更容易处理。所以现在如果没有名字,那就是零。如果您需要稍微改变一下,可以将其保留为“”。

class Thing {
    let name: String?
    let value: Double

    init(name: String?, value: Double){
        self.name = name
        self.value = value
    }

    static func + (lhs: Thing, rhs: Thing) -> Thing? {
        if rhs.name != lhs.name {
            return nil
        } else {
            return Thing(name: lhs.name, value: lhs.value + rhs.value)
        }
    }
}

我给了它一个操作员,因此可以轻松添加它们。它返回一个可选项,因此在使用时要小心。

然后让我们为充满东西的数组做一个方便的扩展:

extension Array where Element: Thing {

func grouped() -> [Thing] {

    var things = [String: Thing]()

    for i in self {
        if let name = i.name {
            things[name] = (things[name] ?? Thing(name: name, value: 0)) + i
        }
    }
    return things.map{$0.1}.sorted{$0.value > $1.value}
}
}

快速测试一下:

let t1 = Thing(name: "a", value: 1)
let t2 = Thing(name: "b", value: 2)
let t3 = Thing(name: "a", value: 1)
let t4 = Thing(name: "c", value: 3)
let t5 = Thing(name: "b", value: 2)
let t6 = Thing(name: nil, value: 10)


let bb = [t1,t2,t3,t4,t5,t6]

let c = bb.grouped()

//(“b”,4),(“c”,3),(“a”,2)

编辑:添加了一个名为nil的示例,该示例由groups()函数中的if let过滤掉