0

I have an array [modelA,modelB,modelC,modelD,modelE], each element in the array is an instance of a Struct. The Struct has a property "name". for example...

modelA.name = "abc"
modelB.name = "efg" 
modelC.name = "hij"    
modelD.name = "abc"
modelE.name = "efg"

How can I group elements with the same property value into a new array? i.e. put modelA and modelD into a new array,and put modelB and modelE into another array.

Assume the original array is large.

Ryan Heitner
  • 13,119
  • 6
  • 77
  • 119
Tom
  • 61
  • 1
  • 6
  • 1
    What would be wrong with `var d[String: [Model]]; for i in array { let m = array.filter { $0.name == i.name }; if m.count > 1 { d[i.name] = m }}`? – Grimxn Jan 18 '17 at 08:29
  • http://stackoverflow.com/questions/31220002/how-to-group-by-the-elements-of-an-array-in-swift this solve my question, thanks for everyone – Tom Jan 18 '17 at 09:32

3 Answers3

2

You can achieve this by using filter(_:):

Returns an array containing, in order, the elements of the sequence that satisfy the given predicate.

For example, consider that the structure looks like:

struct Model {
    var name: String?
}

And you have an array of models:

let allModelsArray = [Model(name: "abc"), Model(name: "efg"), Model(name: "hij"), Model(name: "abc"), Model(name: "efg"), Model(name: "efg"), Model(name: "hij")]

So, you can get your arrays by doing (assuming that you want to filter based on the value of the name):

let abcModelsArray = allModelsArray.filter { $0.name == "abc" }
// [Model(name: Optional("abc")), Model(name: Optional("abc"))]

let hijModelsArray = allModelsArray.filter { $0.name == "hij" }
// [Model(name: Optional("hij")), Model(name: Optional("hij"))]

ALSO:

You mentioned that:

how can I put element which has the same property value into a new array, such as put modelA and modelD into a new array, and put modelB and modelE into a new array, if array is large.

Somehow, you might want to use the lazy version of the collection.

Hope this helped.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • For this you would have to know all the names to begin with. This would be O(n^2) at best. I think @Grimxn comment is a better solution for this and it would be O(n). – Fogmeister Jan 18 '17 at 08:44
  • @Fogmeister - thanks for the comment, but mine is still O(n^2) as `filter` itself is O(n), multiplied by the `for` loop... – Grimxn Jan 18 '17 at 08:53
  • @Grimxn I meant the comment you left. Didn't see your answer. :-) oh. Also, I misread your comment :-) – Fogmeister Jan 18 '17 at 08:54
2

I have not performance tested this

struct Model {
    var type : String
    var name : String
}
var modelA = Model(type: "A", name: "abc")
var modelB = Model(type: "B", name: "efg")
var modelC = Model(type: "C", name: "abc")
var modelD = Model(type: "D", name: "efg")

let models = [modelA,modelB,modelC,modelD]

let names = Set(models.map({return $0.name}))

var groupedModels : [String:[Model]] = [:]
for var name in names {
    let elements = models.filter({$0.name == name})
    groupedModels[name] = elements
}
Ryan Heitner
  • 13,119
  • 6
  • 77
  • 119
  • The good part of your answer that you are assuming that the `name` is unknown, which I find it more logical. – Ahmad F Jan 18 '17 at 09:22
1

.reduce solution:

let a = [modelA, modelB, modelC, modelD, modelE]

let arr = a.reduce([:]) { (result, currentModel) -> [String: [Model]] in
    var mutableDic = result
    if ((mutableDic[currentModel.name]) != nil) {
        mutableDic[currentModel.name]?.append(currentModel)
    } else {
        mutableDic[currentModel.name] = [currentModel]
    }

    return mutableDic
}

It will return the same dictionary as @Grimxn response. or got from this for loop

var mutableDic = [String : [Model]]()

        for aModel in a {
            if ((mutableDic[aModel.name]) != nil) {
                mutableDic[aModel.name]?.append(aModel)
            } else {
                mutableDic[aModel.name] = [aModel]
            }
        }

The key is to use a Dictionary to track for Model that need to be put in the same array, by comparing to it's .name.

Duyen-Hoa
  • 15,384
  • 5
  • 35
  • 44
  • This seems to be a better candidate than mine, as at first look it is O(n). – Grimxn Jan 18 '17 at 09:38
  • Nit picking, but it doesn't exactly return the same as mine, as mine only includes names multiply referenced... – Grimxn Jan 18 '17 at 09:42