将字符串转换为具有多种类型的数组

时间:2017-11-21 20:41:12

标签: arrays swift

Diclaimer:我是Swift的新手,我为我所做的任何明显的疏忽而道歉。

我正在尝试创建一个包含字符串和整数值的2D数组(基于我导入的CSV的内容)。这些可以通过"列来识别"他们在。有没有办法实现这一目标?我知道三种可能的解决方案:

1)将数组声明为[Any],但我不想这样做,因为我希望它是明确键入的

2)使用.flatmap(如How to convert a String (numeric) in a Int array in Swift

let string = "123,456,789"
let intArray = string.components(separatedBy: ",").flatMap { Int($0) }

但我不愿意,因为我可能在" String"中有整数。我阵列的一栏。

3)将我的数组保持为[String]并与我的" Int"列作为字符串(这是我目前正在做的事情)。

以下是我正在使用的代码的简化版本。当我尝试将逗号分隔的字符串解析为具有不同值类型的数组时,它会出错。

非常感谢所有帮助......谢谢!

struct MyStruct {
    let Str1 : String
    let Str2 : String
    let Str3 : String
    let Str4 : String
    let Int1 : Int
}

    func csvStringToArray(stringCSV: String) -> [[MyStruct]] {

        // create an empty 2-D array to hold the CSV data.
        var dataArray: [[MyStruct]] = []

        // parse the CSV into rows.
        var csvRows: [String] = stringCSV.components(separatedBy: "\n") 

        print(csvRows)
        // ["a,b,c,d,99", "j,k,l,m,98", "w,x,y,z,97", etc]

        // append each row (1-D array) to dataArray (filling the 2-D array).
        for i in csvRows {
            let csvColumns: [MyStruct] = i.components(separatedBy: ",") // Cannot convert value of type '[String]' to specified type '[MyStruct]'
            dataArray.append(csvColumns)
            print(csvColumns) // this is the output if I set csvColumns: [String]; otherwise it errors out.
            // ["a", "b", "c", "d", "99"]
            // ["j", "k", "l", "m", "98"]
            // ["w", "x", "y", "z", "97"]
            // etc...
        }
        print(dataArray) // again only if csvColumns: [String]
        // ["a", "b", "c", "d", "99"], ["j", "k", "l", "m", "98"], ["w", "x", "y", "z", "97"], [etc... ]

        return dataArray
    }

3 个答案:

答案 0 :(得分:2)

正如您在评论中指出的那样,这条线不起作用:

let csvColumns: [MyStruct] = i.components(separatedBy: ",")

components(separatedBy:)方法始终返回一个字符串数组([String]),因此您无法将该值设置为一个类型为MyStruct数组的变量({{1} })。

看起来你的MyStruct类型应该代表一整行数据(4个字符串和一个整数),所以我不认为列是MyStructs数组是正确的。第一个原因是这些不代表列,而是行,而且似乎是1 MyStruct == 1行值,而数据数组只是那些MyStruct行值的数组([MyStruct]而不是你现在拥有的MyStruct实例数组([MyStruct])的数组。

那就是说,为了最接近你所拥有的,我建议用一个可以容纳String或Int的FieldValue枚举替换MyStruct。使用这种方法,您的代码将如下所示:

[[MyStruct]]

答案 1 :(得分:1)

您的<div *ngIf="timer"> <ion-item class="no-bottom-border item"> <button ion-button *ngIf="timeInSeconds && timeInSeconds > 0" large full clear class="timer-button timer-text">{{timer.displayTime}}</button> </ion-item> <ion-item class="no-bottom-border" *ngIf="timeInSeconds && timeInSeconds > 0"> <button ion-button icon-left clear color="danger" small (click)="initTimer()" item-left *ngIf="!timer.runTimer && (timer.hasStarted || timer.hasFinished) || timer.hasFinished"> <ion-icon name="refresh"></ion-icon> Reset </button> <button ion-button icon-left clear small color="primary" (click)="pauseTimer()" item-right *ngIf="timer.runTimer && timer.hasStarted && !timer.hasFinished"> <ion-icon name="pause"></ion-icon> Pause </button> <button ion-button icon-left clear small color="primary" (click)="resumeTimer()" item-right *ngIf="!timer.runTimer && timer.hasStarted && !timer.hasFinished"> <ion-icon name="play"></ion-icon> Resume </button> <button ion-button icon-left clear small color="primary" (click)="startTimer()" item-right *ngIf="!timer.hasStarted"> <ion-icon name="play"></ion-icon> Start </button> </ion-item> </div> 已经是一行的结构。 MyStruct的每个元素都是一列。因此,最终您的MyStructdataArray的返回值)应为func csvStringToArray(stringCSV: String),而不是[MyStruct]。然后你的功能变为:

[[MyStruct]]

这显然假设您的数据类型和结构始终有效(您的行总是有4个字符串和一个整数,因此它们可以被索引或强制解包),否则您将不得不为此添加检查。 / p>

答案 2 :(得分:1)

如果您希望完全键入转换并避免Any,请尝试以下方法:

struct MyDataType {

  struct Row {

    enum DataPoint {
      case string(String)
      case integer(Int)

      init?(stringValue: String) {
        guard !stringValue.isEmpty else { return nil }

        if let integerValue = Int(stringValue) {
          self = .integer(integerValue)
        } else {
          self = .string(stringValue)
        }
      }
    }

    let dataPoints: [DataPoint]

    init?(csv: String) {
      let components = csv.components(separatedBy: ",")
      guard !components.isEmpty else { return nil }

      dataPoints = components.flatMap(DataPoint.init)
    }
  }

  let rows: [Row]

  init?(csv: String) {
    let csvRows = csv.components(separatedBy: .newlines)
    guard !csvRows.isEmpty else { return nil }

    rows = csvRows.flatMap(Row.init)
  }
}


// Example usage.

let csvString = """
123,134,a,65,4,bc
43,53,t,4,5,1
a,3,e,12,u,50
"""

//  Force unrwapping here, because we know `csvString` is valid csv.
//  You should not force unwrap generally.
let data = MyDataType(csv: csvString)!

if let firstRow = data.rows.first {

  print("First rows content:")

  for (idx, dataPoint) in firstRow.dataPoints.enumerated() {
    let descriptor: String

    switch dataPoint {
    case .string(let stringValue):
      descriptor = "String: \(stringValue)"
    case .integer(let integerValue):
      descriptor = "Integer: \(integerValue)"
    }

    print("[\(idx)] > \(descriptor)")
    /*
     Prints:


     First rows content:
     [0] > Integer: 123
     [1] > Integer: 134
     [2] > String: a
     [3] > Integer: 65
     [4] > Integer: 4
     [5] > String: bc
     */
  }
}

说明:

  • MyDataType代表所有导入的csv数据。
  • Row表示导入的csv数据中的单行。
  • DataPoint可以包含StringInt。请注意,这与Optional<T>的工作方式非常相似。
  • 每个MyDataType都有多个Row s。
  • 每个Row都有多个DataPoint s。
  • MyDataTypeRowDataPoint声明可以解析csv字符串各自部分的初始化程序。添加了一些安全措施来检查空字符串,但这取决于实现。

更新

在评论中给出了更多上下文,这就是扩展上面提供的通用解决方案并强制执行特定csv格式的方法:

extension MyDataType.Row {
  init?(enforcingCustomFormatOn csv: String) {
    let components = csv.components(separatedBy: ",")
    guard components.count == 5 else { return nil }

    dataPoints = components.enumerated().flatMap { idx, stringValue in
      switch idx {
        //  Enforcing .string in the first 4 columns
      case 0..<4: return .string(stringValue)
      default: return DataPoint(stringValue: stringValue)
      }
    }
  }
}

extension MyDataType {
  init?(enforcingCustomFormatOn csv: String) {
    let csvRows = csv.components(separatedBy: .newlines)
    guard !csvRows.isEmpty else { return nil }

    rows = csvRows.flatMap(Row.init(enforcingCustomFormatOn:))
  }
}

您现在可以简单地使用init(enforcingCustomFormatOn:),第1-4列将被视为字符串值。如果可能,只有第5列将转换为.integer。

请注意,您需要将rows = csvRows.flatMap(Row.init)中的MyDataType.init(csv:)更改为rows = csvRows.flatMap(Row.init(csv:))。否则,.flatMap将无法知道要选择哪个init,因为两者都具有相同的签名。