Split string into Array of Arrays [closed]

2020-05-10 15:09发布

I am writing an iOS App in Swift 4.2

Response from server is a string with values separated by pipe character "|". It contains many rows of values. I want to split it into an array of subarrays.

Response example:

"001|apple|red|002|banana|yellow|003|grapes|purple"

For this example, the output should be an array containing 3 arrays of above fruits. If I use response.componentsSeparatedByString("|") it will give me an array with 9 elements, which I don't want. If I take the above example into consideration, what I need is an array of 3 arrays further having 3 elements.

Expected Output:

[[001, "apple", "red"], [002, "banana", "yellow"], [003, "grapes", "purple"]]

标签: ios swift
6条回答
▲ chillily
2楼-- · 2020-05-10 15:29

With regex you could do something like this with thanks to OOPer's extension

Add a string extension that splits the string based on a regex pattern.

extension String {
    func split(usingRegex pattern: String) -> [String] {
        //### Crashes when you pass invalid `pattern`
        let regex = try! NSRegularExpression(pattern: pattern)
        let matches = regex.matches(in: self, range: NSRange(0..<utf16.count))
        let ranges = [startIndex..<startIndex] + matches.map{Range($0.range, in: self)!} + [endIndex..<endIndex]
        return (0...matches.count).map { String(self[ranges[$0].lowerBound..<ranges[$0+1].lowerBound]) }
    }
}

Then split your string based on the pattern [0-9]{3} 3 numbers in a row.

let str = "001|apple|red|002|banana|yellow|003|grapes|purple|004|this|is|a|test|one|005|so|is|this"
let pattern = "[0-9]{3}"
let result = str.split(usingRegex: pattern)

var all:[[String]] = []
for row in result {
    let split = row.split(separator: "|").map({ (substring) in
        return String(substring)
    })
    if split.count != 0 {
        all.append(split)
    }
}

dump(all)

I tested this in a playground and got the following result:

▿ 5 elements
  ▿ 3 elements
    - "001"
    - "apple"
    - "red"
  ▿ 3 elements
    - "002"
    - "banana"
    - "yellow"
  ▿ 3 elements
    - "003"
    - "grapes"
    - "purple"
  ▿ 6 elements
    - "004"
    - "this"
    - "is"
    - "a"
    - "test"
    - "one"
  ▿ 4 elements
    - "005"
    - "so"
    - "is"
    - "this"

If you decide you want to exclude the ID from the resulting arrays you can modify the extension return to the following:

return (0...matches.count).map { String(self[ranges[$0].upperBound..<ranges[$0+1].lowerBound]) }

This will switch the return range to use the upperBound instead of lowerBound

查看更多
姐就是有狂的资本
3楼-- · 2020-05-10 15:29

You can use String method enumerateSubstrings in range using byWords options, check if the string is an integer, if so append a new array with that string to the result otherwise append the word to the last result array:

let string = "001|apple|red|002|banana|yellow|003|grapes|purple"
var result: [[Substring]] = []
string.enumerateSubstrings(in: string.startIndex..., options: .byWords) { _, range, _, _ in
    let word = string[range]
    if let _ = Int(word) {
        result.append([word])
        // or just a new empty array
        // result.append([])
    } else {
        result[result.index(before: result.endIndex)].append(word)
    }
}

print(result)   // "[["001", "apple", "red"], ["002", "banana", "yellow"], ["003", "grapes", "purple"]]\n"
查看更多
你好瞎i
4楼-- · 2020-05-10 15:44
  1. Split string by | character
  2. Then append elements that not a number in last subarray of resulted array
  3. If element is number, then add new subarray to resulted array
  4. Repeat 2-3 until you done
let input = "001|apple|red|002|banana|yellow|003|grapes|purple"
let result: [[String]] = input
    .split(separator: "|")
    .reduce(into: []) { result, string in
        guard let _ = Int(string) else {
            result[result.count - 1].append(String(string))
            return
        }
        result.append([])
    }
/// result: [["apple", "red"], ["banana", "yellow"], ["grapes", "purple"]]

If you want persist 001 as well, then change result.append([]) to result.append([String(string)]):

[["001", "apple", "red"], ["002", "banana", "yellow"], ["003", "grapes", "purple"]]

Important

This solution expect that your string will start with number or crash otherwise.
If you can't guaranteed that your string starts with number, that you need to manually check that array not empty in guard block.

查看更多
ら.Afraid
5楼-- · 2020-05-10 15:47

If I got correct what you want to receive as a result, then this code would make what you want:

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

let src = "001|apple|red|002|banana|yellow|003|grapes|purple"
let result = src.split(separator: "|").map(String.init).chunked(into: 3)
// result = [["001", "apple", "red"], ["002", "banana", "yellow"], ["003", "grapes", "purple"]]

This will work if you know the expected size of resulting subarrays

You can also remove .map(String.init) from the last line if it's ok for you that array elements are of type String.SubSequence

查看更多
贪生不怕死
6楼-- · 2020-05-10 15:47

Basic way

let str = "001|apple|red|002|banana|yellow|003|grapes|purple"
let components = str.components(separatedBy: "|")
let chunkSize = 3
let loopCount = components.count/chunkSize

var packages: [Array<String>] = []
for index in  0..<loopCount {
    /// Starting index
    let numIndex = index * chunkSize

    /// Get the subarray of range
    let package = Array(components[numIndex..<(numIndex+chunkSize)])
    packages.append(package)
}

print(packages)

Output:

[["001", "apple", "red"], ["002", "banana", "yellow"], ["003", "grapes", "purple"]]

查看更多
Animai°情兽
7楼-- · 2020-05-10 15:49

Okay You can do it recursively

let input = "001|apple|red|002|banana|yellow|003|grapes|purple"

let array = input.components(separatedBy: "|")


// Get Chunks from array
 extension Array {
      func getFirstElements(upTo position: Int) -> Array<Element> {
         let arraySlice = self[0 ..< position]
         return Array(arraySlice)
      }
  }

func recersivelyGetArray (array:[String], slice:inout [[String]]) {

  guard !array.isEmpty else{
    return
  }

  var copyArray = array

  var chunkSize = 3
  if  array.count >= 3 {
    chunkSize = 3
  } else {
    chunkSize = array.count
  }
  let threeElements =  copyArray.getFirstElements(upTo: chunkSize)
  slice.append(threeElements)

  copyArray.removeFirst(chunkSize)

  recersivelyGetArray(array: copyArray, slice: &slice)


}

var inoutResult = [[String]]()

recersivelyGetArray(array: array, slice: &inoutResult)

print(inoutResult) 

Output

Case 1 For Input

let input = "001|apple|red|002|banana|yellow|003|grapes|purple"

[["001", "apple", "red"], ["002", "banana", "yellow"], ["003", "grapes", "purple"]]

Case 2 For Input

let input = "001|apple|red|002|banana|yellow|003|grapes"

[["001", "apple", "red"], ["002", "banana", "yellow"], ["003", "grapes"]]

查看更多
登录 后发表回答