Home > Net >  Swift - Convert array to multidimensional array
Swift - Convert array to multidimensional array

Time:11-27

Those are the array models:

struct UserModel: Codable {
    var userid: Int
    var nickname: String
}

struct UserModelSplit: Codable {
    var usr: [UserModel]
}

Initialising them:

@State private var users = [UserModel]()
@State private var userSplit = [UserModelSplit]()

Getting the first array:

for bla in userReceived{
    users.append(UserModel(userid: bla.userid, nickname: bla.nickname))
}

Now I want to split it to the multidimensional array so the result should be:

userSplit[0][0] // 1th user
userSplit[0][1] // 2th user
userSplit[0][2] // 3th user
userSplit[0][3] // 4th user
userSplit[1][0] // 5th user
userSplit[1][1] // 6th user
userSplit[1][2] // 7th user
userSplit[1][3] // 8th user

I tried all kind of syntaxes and looked up how it could be done without finding anythi useful.

This is the pseudo code which sums up what I've tried:

var current = 0
var added = 0
for val in users{
    userSplit[current][added] = val
    
    added  = 1
    if(added == 3){
        current  = 1
        added = 0
    }
}

This pseudo code is similar to how it would actually work in PHP

I hope it's understandable :D

CodePudding user response:

You are really close, to make a 2D array you can do it like this.

struct UserModel: Codable {
    var userid: Int
    var nickname: String
}

var users : [UserModel] = (1...20).map({ n in
    UserModel(userid: n, nickname: UUID().uuidString)
})

//Change this number to make the sub arrays the size that you want.
let size = 3

//User Split as a 2D Array
var userSplit : [[UserModel]] = stride(from: 0, to: users.count, by: size).map {
    users[$0 ..< Swift.min($0   size, users.count)].map{$0}
}

for splitIdx in userSplit.indices{
    print(splitIdx)
    for userIdx in userSplit[splitIdx].indices {
        print(userSplit[splitIdx][userIdx])
    }
}
 

Using the other model you have it should be something like this...

struct UserModelSplit: Codable {
    var usr: [UserModel]
}
//Array<UserModelSplit>   NOT 2D Array
var userSplit : [UserModelSplit] = stride(from: 0, to: users.count, by: size).map {
    UserModelSplit(usr: users[$0 ..< Swift.min($0   size, users.count)].map{$0})
}

for splitIdx in userSplit.indices{
    print(splitIdx)
    for userIdx in userSplit[splitIdx].usr.indices {
        print(userSplit[splitIdx].usr[userIdx])
    }
}

With both of those scenarios you will get print out that looks something like

enter image description here

The biggest reason your code doesn't work is because in swift you can only use

array[index] = something 

to replace an existing value.

You have to

array.append(something)

when you are creating a new index.

You can split any array into a 2D array with an Array extension

extension Array {
    func split(_ size: Int) -> [[Element]] {
        return stride(from: 0, to: count, by: size).map {
            Array(self[$0 ..< Swift.min($0   size, count)])
        }
    }
}

Then use

//User Split as a 2D Array
var userSplit : [[UserModel]] = users.split(size)

CodePudding user response:

You can use sequence method to iterate through your collection and initialize each subsequence into a new collection of its elements:

extension Collection {
    var quadruplets: [[Element]] {
        .init(
            sequence(state: startIndex) { start in
                guard start < endIndex else { return nil }
                let end = index(start, offsetBy: 4, limitedBy: endIndex) ?? endIndex
                defer { start = end }
                return .init(self[start..<end])
            }
        )
    }
}

Usage:

struct UserModel: Codable {
    let userid: Int
    let nickname: String
}

let users = [
    (1,"a"),
    (2,"b"),
    (3,"c"),
    (4,"d"),
    (5,"e"),
    (6,"f"),
    (7,"g"),
    (8,"h"),
    (9,"i"),
    (10,"j"),
].map(UserModel.init)

let userSplit = users.quadruplets

userSplit[0][0].nickname  // "a"
userSplit[0][1].nickname  // "b"
userSplit[0][2].nickname  // "c"
userSplit[0][3].nickname  // "d"
userSplit[1][0].nickname  // "e"
userSplit[1][1].nickname  // "f"
userSplit[1][2].nickname  // "g"
userSplit[1][3].nickname  // "h"
userSplit[2][0].nickname  // "i"
userSplit[2][1].nickname  // "j"

edit/update:

If you don't want to duplicate the collection elements you can just iterate the original collection subsequences. Something like:

extension Collection {
    func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence<SubSequence,Index> {
        sequence(state: startIndex) { start in
            guard start < endIndex else { return nil }
            let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
            defer { start = end }
            return self[start..<end]
        }
    }
    func subSequences(limitedTo maxLength: Int) -> [SubSequence] { .init(unfoldSubSequences(limitedTo: maxLength))
    }
}

Usage:

let subSequences = users.subSequences(limitedTo: 4)

subSequences[0][0].nickname  // "a"
subSequences[0][1].nickname  // "b"
subSequences[0][2].nickname  // "c"
subSequences[0][3].nickname  // "d"
subSequences[1][4].nickname  // "e"
subSequences[1][5].nickname  // "f"
subSequences[1][6].nickname  // "g"
subSequences[1][7].nickname  // "h"
subSequences[2][8].nickname  // "i"
subSequences[2][9].nickname  // "j"

for subsequence in subSequences {
    for user in subsequence {
        print(user)
    }
}

This will print

UserModel(userid: 1, nickname: "a")
UserModel(userid: 2, nickname: "b")
UserModel(userid: 3, nickname: "c")
UserModel(userid: 4, nickname: "d")
UserModel(userid: 5, nickname: "e")
UserModel(userid: 6, nickname: "f")
UserModel(userid: 7, nickname: "g")
UserModel(userid: 8, nickname: "h")
UserModel(userid: 9, nickname: "i")
UserModel(userid: 10, nickname: "j")


And to make it complete you can extend RangeReplaceableCollection as follow. This would work with Strings as well (Collection of Characters):

extension RangeReplaceableCollection {
    func groups(limitedTo maxLength: Int) -> [Self] {
        subSequences(limitedTo: maxLength).map(Self.init)
    }
}

let quadruplets = "abcdefghij".groups(limitedTo: 4)

quadruplets[0]  // "abcd"
quadruplets[1]  // "efgh"
quadruplets[2]  // "ij"
  • Related