RConsortium / S7

S7: a new OO system for R
https://rconsortium.github.io/S7
Other
386 stars 32 forks source link

Modify data in a property upon instantiation #368

Closed jeffkimbrel closed 11 months ago

jeffkimbrel commented 11 months ago

I have an S7 class where a user will upload a data frame into a slot called @data, and then there are other slots where a user can define the column names where other expected data will be found. What I want to do is take their data frame and do an actual dplyr::rename to change the column name in @data from the name they provide to a standardized column name, modifying the @data between when it is given to the S7 instantiation and actually saved to the new class. Is it possible to do such modifications?

For example, if a user provides mtcars with the hp column and I want to change it to horsepower during instantiation...

cars_object <- S7::new_class(
  "cars_object",
  properties = list(
    data = S7::class_data.frame,
    horsepower_column_name = S7::class_character
  )
)

mtcars_s7 = cars_object(mtcars, 
                        horsepower_column_name = "hp")

I can modify the S7 object afterward with:

mtcars_s7@data = rename(mtcars_s7@data, 
                        horsepower = mtcars_s7@horsepower_column_name)

but is there any way to put it in the actual new_class call? Maybe part of a custom constructor or something?

Alternatively, is it better to make a wrapper around the new_class call to handle this? This does work, but is it recommended to use such wrappers around the creation of S7 objects?

create_cars_object = function(data, horsepower_column_name) {

  # rename columns
  data = rename(data, 
                horsepower = horsepower_column_name)

  # then make object with new column name
  cars_object(data, 
              horsepower_column_name = "horsepower")

}

create_cars_object(mtcars, 
                   horsepower_column_name = "hp")

Thanks for any help.

hadley commented 11 months ago

Have you tried using a custom constructor?

jeffkimbrel commented 11 months ago

Thanks, that was obvious but not so obvious. All of the example custom constructors I could find only had a new_object call, and I didn't realize more code could be added. I also didn't realize the arguments to the constructor are the props, and that multiple could be passed. So, this code works:

cars_object2 <- S7::new_class(
  "cars_object2",
  properties = list(
    data = S7::class_data.frame,
    horsepower_column_name = S7::class_character
  ),
  constructor = function(data, horsepower_column_name) {
    data = dplyr::rename(data, 
                  horsepower = horsepower_column_name)

    S7::new_object(S7::S7_object(),
               data = data,
               horsepower_column_name = horsepower_column_name)
  }
)

Thanks again for the help! Also, would you consider opening up the "discussions" portion of the repo? This wasn't really an "issue" and might have fit better under a discussion.