Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not able to build on last xcode, swift 4.1 #15

Open
KanybekMomukeyev opened this issue Apr 22, 2018 · 5 comments
Open

Not able to build on last xcode, swift 4.1 #15

KanybekMomukeyev opened this issue Apr 22, 2018 · 5 comments

Comments

@KanybekMomukeyev
Copy link

extension CSVParser: RangeReplaceableCollection {
  public func replaceSubrange<C>(_ subrange: Range<Int>, with newElements: C) where C : Collection, C.Iterator.Element == Array<String> {
    self._rows.replaceSubrange(subrange, with: newElements)
  }
  public func reserveCapacity(_ n: Int) {
    self._rows.reserveCapacity(n)
  }
}

Here i have got error.

@KanybekMomukeyev KanybekMomukeyev changed the title Not build on last xcode, swift 4.1 Not able to build on last xcode, swift 4.1 Apr 22, 2018
@mafer-bc
Copy link

mafer-bc commented Apr 29, 2018

Having the exact same issue.
The compiler error is:
Type 'CSVParser' does not conform to protocol 'RangeReplaceableCollection'

~/Swift.RangeReplaceableCollection:3:24: Candidate has non-matching type 'S' [with SubSequence = CSVParser.SubSequence]

But it doesn't specify which methods are missing to conform to the protocol.
Right now I commented that extension, so I can run my project, but obviously, that eventually will break something!

Any help?

@SubramanyamKaluva
Copy link

SubramanyamKaluva commented Jul 11, 2018

Not able to use with current swift4 syntax. Carthage update fails.

any ideas to make update? Thank you.

@KanybekMomukeyev
Copy link
Author

Look to Pull request:
jeffypooo@ceaeea4

@KanybekMomukeyev
Copy link
Author

import Foundation

public class CSVParser {
  
  var content: String
  var _rows:   [[String]]
  
  var rows: [[String]] {
    get {
      if let lastElement = _rows.last, lastElement == [""] {
        return Array(_rows.dropLast())
      }
      return _rows
    }
  }
  
  let hasHeader:     Bool
  // config
  let delimiter:     Character
  let lineSeparator: Character
  let quotes:        Character = "\""
  
  var headers: [String] {
    get {
      if hasHeader {
        return self.rows.first ?? []
      } else {
        return []
      }
    }
  }
  
  /**
    Create a CSVParser from String
   - Parameters:
    - content: the CSV String to parse
    - delimiter: the delimiter of the csv string
    - lineSeparator: the line separator of the csv string
  */
  public init(
      content: String,
      delimiter: Character = ",",
      lineSeparator: Character = "\n",
      hasHeader: Bool = true
  ) throws {
    self.content = content
    self.delimiter = delimiter
    self.lineSeparator = lineSeparator
    self._rows = []
    self.hasHeader = hasHeader
    try self.parse()
  }
  
  /**
   Create a CSVParser from String
   - Parameters:
     - content: the CSV String to parse
     - delimiter: the delimiter of the csv file
     - lineSeparator: the line separator of the csv file
   */
  public convenience init(filePath: String, delimiter: Character = ",", lineSeparator: Character = "\n") throws {
    let fileContent = try String(contentsOfFile: filePath)
    try self.init(content: fileContent, delimiter: delimiter, lineSeparator: lineSeparator)
  }
  
  /**
   Create a CSVParser from [[String]]
   - Parameters:
   - elements: the elements in the csv file
   - delimiter: the delimiter of the csv file
   - lineSeparator: the line separator of the csv file
   */
  public init(
      elements: [[String]],
      delimiter: Character = ",",
      lineSeparator: Character = "\n",
      hasHeader: Bool = true
  ) {
    self.content = ""
    self.delimiter = delimiter
    self.lineSeparator = lineSeparator
    self._rows = elements
    self.hasHeader = hasHeader
  }
  
  /**
   Required by 'RangeReplaceableCollection'
   */
  public convenience required init<S: Sequence>(_ elements: S) where S.Element == [String] {
    self.init(elements: [[String]](elements))
  }
  
  /**
  Required by 'RangeReplaceableCollection'
  */
  public convenience required init() {
    self.init(elements: [])
  }
  
  public func wirite(toFilePath path: String) throws {
    try self.rows.map { $0.joined(separator: String(self.delimiter)) }
        .joined(separator: String(self.lineSeparator))
        .write(to: URL(fileURLWithPath: path), atomically: false, encoding: .utf8)
  }
  
  public func enumeratedWithDic() -> [[String: String]] {
    return self.rows.dropFirst().map {
      var dic = [String: String]()
      for (index, word) in $0.enumerated() {
        dic[self.headers[index]] = word
      }
      return dic
    }
  }
  
  private func parse() throws {
    if let _ = self.content.range(of: String(self.quotes)) {
      // if the file contains quote '"'
      try self.parseWithQuotes()
    } else {
      // if the file not contain quote
      self.parserNoQuote()
    }
  }
  
  private func functionalParse() {
    if let _ = self.content.range(of: String(self.quotes)) {
      // if the file contains quote '"'
      self.functionalParseWithQuote()
    } else {
      // if the file not contain quote
      self.parserNoQuote()
    }
  }
  
  // MARK: CSV To JSON
  /**
   Convert csv to JSON
   
   The return json type
   [
   {
   "header0": "a",
   "header1": "b"
   },
   {
   "header0": "a",
   "header1": "b"
   }
   ]
   
   - Returns: the parsed json string
   */
  public func toJSON() throws -> String? {
    let dic      = self.enumeratedWithDic()
    let jsonData = try JSONSerialization.data(withJSONObject: dic, options: .prettyPrinted)
    let jsonStr  = String(data: jsonData, encoding: String.Encoding.utf8)
    return jsonStr
  }
  
  //MARK: JSON TO CSV
  /**
   Static method Convert Json to csv string
   You can use result to generate a CSVParser instance
   
   The json input now only suport this json type
   [
   {
   "header0": "a",
   "header1": "b"
   },
   {
   "header0": "a",
   "header1": "b"
   }
   ]
   
   - Parameter: jsonData: the json object with Data type.
   - Returns: the parsed CSV String
  */
  static public func jsonToCSVString(jsonData: Data) throws -> String {
    guard let jsonObj = try JSONSerialization.jsonObject(
        with: jsonData,
        options: .allowFragments
    ) as? Array<Dictionary<String, Any>> else {
      throw CSVParserError.jsonObjTypeNotMatch
    }
    let delimiter     = ","
    let lineSeparator = "\n"
    if jsonObj.count == 0 {
      return ""
    }
    let header = jsonObj[0].keys
    let headerStr = header.dropFirst().reduce(header.first!) {
      result, col in
      result + delimiter + col
    }
    // help method
    // parse dic to a line of csv string
    func dicToStr(dic: [String: Any]) -> String {
      var result = lineSeparator
      for key in header {
        result = result + parseDicValue(value: dic[key]) + delimiter
      }
      result.remove(at: result.index(before: result.endIndex))
      return result
    }
    
    // help method
    // parse dic value to string
    func parseDicValue(value: Any?) -> String {
      if let value = value as? String {
        return value
      } else if let intValue = value as? Int {
        return String(intValue)
      } else if let floatValue = value as? Float {
        return String(floatValue)
      }
      return ""
    }
    
    let csvContent = jsonObj.reduce(headerStr) {
      (result, row) -> String in
      result + dicToStr(dic: row)
    }
    return csvContent
  }
  
}

//MARK: Make a CSVParserIterator
public struct CSVParserIterator: IteratorProtocol {
  
  public typealias Element = [String]
  
  var rowsIterator: IndexingIterator<[[String]]>
  
  init(rows: [[String]]) {
    self.rowsIterator = rows.makeIterator()
  }
  
  public mutating func next() -> [String]? {
    return self.rowsIterator.next()
  }
  
}

//MARK: Comfirm to Sequence protocol
extension CSVParser: Sequence {
  public typealias SubSequence = ArraySlice<[String]>
  
  public func makeIterator() -> CSVParserIterator {
    return CSVParserIterator(rows: self.rows)
  }
}

//MARK: Comfirm to Collection protocol
extension CSVParser: Collection {
  public typealias Index = Int
  public typealias Element = [String]
  public var startIndex: Index { return self.rows.startIndex }
  public var endIndex:   Index {
    return self.rows.endIndex
  }
  
  public func index(after i: Index) -> Index {
    return self.rows.index(after: i)
  }
  
  public subscript(position: Index) -> [String] { return self._rows[position] }
  
  public subscript(bounds: Range<Int>) -> ArraySlice<[String]> { return self._rows[bounds] }
  
}

extension CSVParser {
  /**
   The String subscript
   - Returns: the column
   */
  public subscript(key: String) -> [String]? {
    guard let index = self.headers.index(of: key) else {
      return nil
    }
    // must parse first
    return self.rows.dropFirst().map {
      // make sure every column
      if index >= $0.count {
        return ""
      } else {
        return $0[index]
      }
    }
  }
}

extension CSVParser: RangeReplaceableCollection {
  
  public func replaceSubrange<C>(_ subrange: Range<Int>, with newElements: C) where C: Collection, Element == C.Element {
    self._rows.replaceSubrange(subrange, with: newElements)
  }
  
  public func append<S>(contentsOf newElements: S) where S: Sequence, CSVParser.Element == S.Element {
    self._rows.append(contentsOf: newElements)
  }
  
}

@SubramanyamKaluva
Copy link

@KanybekMomukeyev

Thank you!! Do you have any idea, how to generate a framework using the above code? I use Carthage update to build the framework and it automatically adds to the project bundle. Please help?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants