How to update data in a DataFrame

My project loads a CSV into a DataFrame and displays it in a Table (a MacOS app). So far so good ... but when trying to update a value in a column, I dont see anyway to update this value.

The table gets the value for the column like this:

 func getColumnValue(row :DataFrame.Rows.Element, columnName :String) -> String
 {
  if row.base.containsColumn(columnName)
  {
   var value = ""
   
   if row[columnName] != nil
   {
    value = "\(row[columnName]!)"
    }

   return value
   }
   ...

But the documentation and googles dont show any way to update the same column. Any help is appreciated with cookies.

Attempt to update:

 func setColumnValue(row :DataFrame.Rows.Element, columnName :String, value :String)
 {
  var column: [String?] = data[columnName]
  
  column[row.id] = value
 ...
  }
Answered by DTS Engineer in 816375022

I’m not 100% sure I understand your question, but if you just want to modify a value at a known column / row then you can do that using subscripts. Here’s a simple example that corrects the publication date of Planet of Exile:

import Foundation
import TabularData

let novels60s = """
    "Title","Year"
    "Rocannon's World","1966"
    "Planet of Exile","1967"
    "City of Illusions","1967"
    "A Wizard of Earthsea","1968"
    "The Left Hand of Darkness","1969"
    """

func test() throws {
    var df = try DataFrame(csvData: Data(novels60s.utf8))
    print("before:\n\(df)")
    df["Year"][1] = 1966
    print("after:\n\(df)")
}

try test()

It prints:

before:
┏━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃   ┃ Title                     ┃ Year  ┃
┃   ┃ <String>                  ┃ <Int> ┃
┡━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ 0 │ Rocannon's World          │ 1,966 │
│ 1 │ Planet of Exile           │ 1,967 │
│ 2 │ City of Illusions         │ 1,967 │
│ 3 │ A Wizard of Earthsea      │ 1,968 │
│ 4 │ The Left Hand of Darkness │ 1,969 │
└───┴───────────────────────────┴───────┘
5 rows, 2 columns

after:
┏━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃   ┃ Title                     ┃ Year  ┃
┃   ┃ <String>                  ┃ <Int> ┃
┡━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ 0 │ Rocannon's World          │ 1,966 │
│ 1 │ Planet of Exile           │ 1,966 │
│ 2 │ City of Illusions         │ 1,967 │
│ 3 │ A Wizard of Earthsea      │ 1,968 │
│ 4 │ The Left Hand of Darkness │ 1,969 │
└───┴───────────────────────────┴───────┘
5 rows, 2 columns

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I’m not 100% sure I understand your question, but if you just want to modify a value at a known column / row then you can do that using subscripts. Here’s a simple example that corrects the publication date of Planet of Exile:

import Foundation
import TabularData

let novels60s = """
    "Title","Year"
    "Rocannon's World","1966"
    "Planet of Exile","1967"
    "City of Illusions","1967"
    "A Wizard of Earthsea","1968"
    "The Left Hand of Darkness","1969"
    """

func test() throws {
    var df = try DataFrame(csvData: Data(novels60s.utf8))
    print("before:\n\(df)")
    df["Year"][1] = 1966
    print("after:\n\(df)")
}

try test()

It prints:

before:
┏━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃   ┃ Title                     ┃ Year  ┃
┃   ┃ <String>                  ┃ <Int> ┃
┡━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ 0 │ Rocannon's World          │ 1,966 │
│ 1 │ Planet of Exile           │ 1,967 │
│ 2 │ City of Illusions         │ 1,967 │
│ 3 │ A Wizard of Earthsea      │ 1,968 │
│ 4 │ The Left Hand of Darkness │ 1,969 │
└───┴───────────────────────────┴───────┘
5 rows, 2 columns

after:
┏━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃   ┃ Title                     ┃ Year  ┃
┃   ┃ <String>                  ┃ <Int> ┃
┡━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ 0 │ Rocannon's World          │ 1,966 │
│ 1 │ Planet of Exile           │ 1,966 │
│ 2 │ City of Illusions         │ 1,967 │
│ 3 │ A Wizard of Earthsea      │ 1,968 │
│ 4 │ The Left Hand of Darkness │ 1,969 │
└───┴───────────────────────────┴───────┘
5 rows, 2 columns

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks Eskimo for this ... I will apply the changes as you outlined !

Did not work .... the data table, is creating a dynamic field, based on the contents it is loading:

@ViewBuilder func createKeyField(row :DataFrame.Rows.Element, columnName :String) -> some View { let binding = Binding<String>( get: { dbModel.getColumnValue(row :row, columnName :columnName) }, set: { dbModel.setColumnValue(row :row, columnName :columnName, value :$0) } )

TextField("", text: binding) }

func setColumnValue(row :DataFrame.Rows.Element, columnName :String, value :String) { data[columnName][row.id] = value }

but the value does not get changed

So, lemme see if I understand you correctly:

  • The data in the DataFrame is changing.

  • But your SwiftUI table is not updating to reflect that change.

Is that right?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

That's correct - and while using Observables for the model, when I key in the text field, you see the value, but immediately the entire table refreshes.

Accepted Answer

OK. This has strayed into SwiftUI territory, which isn’t my area of expertise. I’ve retagged the thread to see if we can attract the attention of any SwiftUI experts.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

How to update data in a DataFrame
 
 
Q