Skip to content

Add an alias for functional version of := #3795

Closed

Description

@eliocamp

Right now when adding multiple columns to a data.table one of the options is to do:

dt[, `:=`(col1 = stuff, col2 = moar_stuff)]

I think that this syntax can be a bit confusing (too many equals) but mainly is hard to write. To get the grave accent, on my keyboard I have to press AltGr + } and then space. It's kind of annoying.

So my modest proposal is to just have an alias for :=() that is just a run-off-the-mill common name with no special characters. Maybe (ab)using the set() function? It would end up something like this:

dt[, set(col1 = stuff, col2 = moar_stuff)]

I think it not only reads better, but it's also 87% easier to type.

Activity

  1. MichaelChirico commented on Aug 28, 2019

    @MichaelChirico
    Member

    Relatedly it would also help solve this issue:

    #1543 (comment)

    (I suggested the name list_set there but the idea is the same)

    I think the issue is there's a reason why := is not defined as a proper function (limiting the API so prevent its being used outside [)... not sure how challenging it is to build out the NSE hacks to work for a new function in addition to :=.

    on my keyboard I have to press AltGr + } and then space. It's kind of annoying.

    Sounds very much so! Are you working in RStudio? There must be a way to define some add-in keyboard shortcuts...

  2. eliocamp commented on Aug 28, 2019

    @eliocamp
    ContributorAuthor

    Sounds very much so! Are you working in RStudio? There must be a way to define some add-in keyboard shortcuts...

    That was going to be my second proposal. I've just made one and was thinking if it would make sense to add it to data.table proper.

    add_dot_equal <- function() {
      rstudioapi::insertText(text = "`:=`()")
      pos <- rstudioapi::getActiveDocumentContext()$selection[[1]]$range
      pos$start[2] <- pos$start[2] - 1
      pos$end[2] <- pos$end[2] - 1
      rstudioapi::setCursorPosition(pos)
    }
  3. eliocamp commented on Aug 28, 2019

    @eliocamp
    ContributorAuthor

    I've just realised that this would also make it possible to add columns by reference without explicitly adding names. That is, if list_fun() is a function that returns a named list then you could do something like

    dt[, set(list_fun(columns))] 

    I think it would be cool to stick to the naming convention of starting with set to indicate that is changing something by reference. set_columns()? (too long).

    As far as implementation, I have no idea xD

  4. jangorecki commented on Aug 29, 2019

    @jangorecki
    Member

    Note that there is alternative API for ":="(c("col1","col2"), list(col1, col2)).

    I would prefer to avoid name collision, even if not evaluated. Maybe setj?

    As for the named list in :=, isn't it better to just handle ":="(list(col1=col1, col2=col2))?

  5. eliocamp commented on Aug 29, 2019

    @eliocamp
    ContributorAuthor

    As for the named list in :=, isn't it better to just handle ":="(list(col1=col1, col2=col2))?

    Yes, I guess ultimately it's the same change.

    setj() could work, but the "j" there is kind of esoteric and I don't know how many users (specially beginners) appreciate that they are using an argument named "j".

  6. jangorecki commented on Sep 21, 2019

    @jangorecki
    Member

    setj() could work, but the "j" there is kind of esoteric and I don't know how many users (specially beginners) appreciate that they are using an argument named "j".

    Once they will figure that out, looking at ?"[" for example, they will appreciate. These really basics things that anyone who wants to "know R" should learn at some point.


    My suggestion to address this issue is an alias setj which will be substituted for := in [.data.table.

  7. eliocamp commented on Sep 21, 2019

    @eliocamp
    ContributorAuthor

    I don't know if many users will ever look at ?"[" (I now I never have 😜️). Still, I generally like more descriptive arguments. "j" is basically meaningless --merely a convention from algebra notation-- whereas "column" actually describes what's going on.
    How abut setcol()?

  8. tdeenes commented on Sep 23, 2019

    @tdeenes
    Member

    I think most (if not all) set* functions take a data.table as input (e.g., setkey, setorder, setcolorder). The most natural syntax to me would be:

    DT[, .(col1 := new_value, col2 := something)]

    This way no new symbol is introduced; we simply allow for mixing the already well-known .() (~the result consists of multiple columns) and := (~do the assignment in place) symbols.

  9. eliocamp commented on Sep 23, 2019

    @eliocamp
    ContributorAuthor

    In my opinion, that would be more confusing because then the behaviour of .() would not be consistent. Sometimes it would create a new data.table and others it would modify by reference. It could further come into problems if someone where to write DT[, .(col1 := new_value, col2 = something)] and the functional version would still be needed for the ":="(list(col1=col1, col2=col2)) case.

    I do think its a good point that set* functions take a data.table as argument, though. 🤔

  10. jangorecki commented on Sep 23, 2019

    @jangorecki
    Member

    setattr works for other types

  11. eliocamp commented on Sep 23, 2019

    @eliocamp
    ContributorAuthor

    Yeah, but I think the point is that set functions modify (by reference) the object in the x argument. In this case set would be modifying the data.table in which it is called. It's a small departure from the other functions' behaviour, maybe.

  12. dracodoc commented on Oct 26, 2019

    @dracodoc
    Contributor

    Maybe simply use a different word other than set? How about let?

  13. eliocamp commented on Oct 27, 2019

    @eliocamp
    ContributorAuthor

    That's fine by me. Is not bad.

  14. moodymudskipper commented on Nov 5, 2019

    @moodymudskipper

    As far as I can tell it's only about changing == ":=" to %in% c(":=", "let") in the code.

    I wrote this evil hack for a reddit user that wanted this feature :

    set_colon_equal_alias <- function(alias){
      requireNamespace("data.table")
      temp <- data.table:::`[.data.table`
      body(temp)[-1] <- parse(text = gsub(
        '== ":="',
        paste("%in%", deparse(c(":=", alias))) ,
        body(temp)[-1],
        fixed = TRUE))
      assignInNamespace("[.data.table", temp, "data.table")
      invisible()
    }
    
    library(data.table)
    dt <- data.table(x = c(1,2,3), y = c(4,5,6))
    set_colon_equal_alias("let")
    
    dt[, let(double_x = x * 2, x_plus_y = x + y)][]
    #>    x y double_x x_plus_y
    #> 1: 1 4        2        5
    #> 2: 2 5        4        7
    #> 3: 3 6        6        9
    
  15. s-fleck commented on Dec 17, 2019

    @s-fleck

    Might I suggest "mut()"? Seems appropriate for mutating a mutable object :)

  16. mattdowle commented on Nov 30, 2021

    @mattdowle
    Member

    To get the grave accent, on my keyboard I have to press AltGr + } and then space. It's kind of annoying.

    Btw, just for completeness, both single quote and double quote also work; i.e. DT[, ':='(...)] and DT[, ":="(...)]. It doesn't have to be backtick; i.e. grave. Not saying an alias isn't still better. Will merge PR. Just saying for completeness it doesn't have to be backtick.

  17. added this to the 1.14.3 milestone on Nov 30, 2021
  18. modified the milestones: 1.14.9, 1.15.0 on Oct 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions