{"id":32388845,"url":"https://github.com/seasmith/r-wishlist","last_synced_at":"2025-10-25T03:56:08.857Z","repository":{"id":264228737,"uuid":"178502791","full_name":"seasmith/r-wishlist","owner":"seasmith","description":"Things I wish base R could have","archived":false,"fork":false,"pushed_at":"2020-10-25T04:02:33.000Z","size":53,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-22T19:27:35.515Z","etag":null,"topics":["base-r","r","rstats"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seasmith.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-30T02:46:45.000Z","updated_at":"2020-10-25T04:02:35.000Z","dependencies_parsed_at":"2024-11-22T21:58:13.799Z","dependency_job_id":null,"html_url":"https://github.com/seasmith/r-wishlist","commit_stats":null,"previous_names":["seasmith/r-wishlist"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/seasmith/r-wishlist","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seasmith%2Fr-wishlist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seasmith%2Fr-wishlist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seasmith%2Fr-wishlist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seasmith%2Fr-wishlist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seasmith","download_url":"https://codeload.github.com/seasmith/r-wishlist/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seasmith%2Fr-wishlist/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280901444,"owners_count":26410586,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["base-r","r","rstats"],"created_at":"2025-10-25T03:56:07.510Z","updated_at":"2025-10-25T03:56:08.848Z","avatar_url":"https://github.com/seasmith.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# R Wishlist\nThings I wish base R could have\n\n## Table of Contents\n* [New Functions](#new-functions)\n  * [Assignment](#assignment)\n  * [Vectors](#vectors)\n  * [Numbers](#numbers)\n  * [Logicals](#logicals)\n  * [Dates And Time](#dates-and-time)\n  * [Files](#files)\n  * [Environments](#environments)\n* [New Behavior](#new-behavior)\n* [Argument Order](#argument-order)\n* [Remove](#remove)\n\n## New Functions\n\n### Assignment\n\n* `:=` = deconstruction operator\n* `{var} \u003c- obj` = alternative deconstruction syntax\n  - Should probably include global option `deconstruction.overload` with values `\"recycle\"`, `\"start\"`, and `\"end\"` to determine how unequal number of `rhs`/`lhs` elements should be treated.\n  \n```r\n{a} \u003c- c(1)\n{a, b} \u003c- c(1, 2)\n\n# default recycle\n{a, b, x} \u003c- c(1, 2, 3, 4, 5)\nprint(a)\n# [1] 1 4\nprint(b)\n# [1] 2 5\n```\n\n* `\u003c-\u003e` = compound assignment\n\n```r\n# := is a valid but undefined infix operator.\n# This implementation does not use a base R solution!\n\n`:=` \u003c- zeallot::`%\u003c-%`\n\n# I have only heard rumors of _'s validity,\n# but have yet to see this applied.\n# So, there is no way to implement a compound\n# assignment operator with _.\n\n# I chose \u003c-\u003e becuase I currently like it\n# but thought mentioning _ was good.\n```\n\n### Vectors\n\nThese functions apply to (just about) any vector.\n\n* `n_duplicated()` = the number of duplicated elements (elements which are duplicates of some other element)\n\n```r\nn_duplicated \u003c- function (x, na.rm = FALSE, incomparables = FALSE, ...) {\n    \n    x \u003c- if (na.rm) c(na.omit(x)) else x\n    sum(duplicated(x, incomparables = incomparables, ...))\n    \n}\n```\n\n### Numbers\n\n* `rep_each()` = the `each` argument in `rep()` is not obvious from the docs; we already have `rep_len()`; see also  `rep()` in [new behavior](#new-behavior)\n\n```r\n# Possible implementations\n\nrep_each \u003c- function (x, each) rep(x, each = each)\n\n# Faster, from: https://twitter.com/BrodieGaslam/status/1206765767775203328\nrep_each \u003c- function(x, each) {\n  if(each) {\n    res \u003c- matrix(x, nrow=each, ncol=length(x), byrow=TRUE)\n    dim(res) \u003c- NULL\n    res\n  } else x[0]\n}\n```\n\n* `rm_na()` = remove NA placeholders in a vector; `na.omit()` returns a new class and attribute that is undesired in some context (i.e. printing).\n\n```r\n# Possible implementation\n\nrm_na \u003c- function(x) c(na.omit(object = x))\n```\n\n* `index()` -- index or normalize a vector (either to the min and max, or to the start\n\n```r\nindex \u003c- function (x, method = \"min-max\") \n{\n    x \u003c- switch(method,\n      `min-max` = {\n        min_x \u003c- min(x, na.rm = TRUE)\n        max_x \u003c- max(x, na.rm = TRUE)\n        vapply(X = x,\n               FUN = function(i) (i - min_x)/(max_x - min_x),\n               FUN.VALUE = numeric(1))},\n      start = {\n        first_x \u003c- x[1]  # assume first is start\n        vapply(X = x,\n               FUN = function(i) (i - first_x)/first_x, \n               FUN.VALUE = numeric(1))\n      })\n    x\n}\n```\n\n* `cumpct()` -- cumulative percent (ascending - from smallest to largest)\n\n```r\ncumpct \u003c- function (x) {\n  i \u003c- order(x)\n  j \u003c- order(i)\n  xi \u003c- x[i]\n  res \u003c- cumsum(xi) / sum(xi)\n  res[j]\n}\n\nd \u003c- c(25, 10, 50, 15)\ncumpct(d)\n# [1] 0.50 0.10 1.00 0.25\n```\n\n* `nmax()`/`nmin()` -- min/max with offset; can take first max, second max, etc\n\n```r\nx \u003c- c(2, 4, 3, 5)\n\nnmax(x) # default 0 offset\n# [1] 5\nnmax(x, offset = 1)\n# [2] 4\n```\n\n### Logicals\n\n* `notNA()` = compliment to `is.na()`\n* `allNA()` = are all elements `NA`?\n* `noneNA()` = compliment to `allNA()`\n\n```r\n# Possible implementations\n\nnotNA  \u003c- function (x) !is.na(x)\nallNA  \u003c- function (x) all(is.na(x) == TRUE)\nnoneNA \u003c- function (x) all(is.na(x) == FALSE)\n```\n\n* `none()` = compliment to `all()`\n* `allFALSE()` = opposite of `all()`\n* `nor()` = neither are `TRUE`; binary case of `!all()`/`none()`\n\n```r\n# Possible implementations\n\nnone \u003c- function(...) all(... != TRUE)\nallFALSE \u003c- function(...) all(... == FALSE)\nnor \u003c- function (x, y) all(x != TRUE, y != TRUE)\n```\n\n### Dates And Time\n\n* `is.POSIXct()` = datetime equivalent to `is.Date()`\n* `is.POSIXlt()` = ...\n* `is.POSIXt()` = ...\n\n```r\n# Possible implementations\n\nis.POSIXct \u003c- function (x) inherits(x, \"POSIXct\")\nis.POSIXlt \u003c- function (x) inherits(x, \"POSIXlt\")\nis.POSIXt \u003c- function (x) inherits(x, \"POSIXt\")\n```\n\n### Lists\n\n* `list_collapse(x, index)` -- collapse a list's components by an index vector\n\n```r\n# Possible implementations\n\nl \u003c- rep(list(1:3, 4:5), 2)\n\n# -- Using split + lapply -- #\nlist_collapse \u003c- function (x, index) {\n    lapply(split(x, index), function (j) Reduce(append, j))\n}\n\ncollapse_index1 \u003c- rep(1:2, times = 2)\ncollapse_index2 \u003c- rep(1:2, each = 2)\n\nlist_collapse(l, collapse_index1)\nlist_collapse(l, collapse_index2)\n\n# -- Using tapply -- #\n\nlist_collapse2 \u003c- function (x, index) tapply(x, index, function (j) Reduce(append, j))\n\nlist_collapse2(l, collapse_index1)\nlist_collapse2(l, collapse_index2)\n\n# -- Inner function -- #\n# The function function (j) Reduce(append, j) \n# is the real workhorse. Can test against using\n# c in place of append. Also test list_collapse\n# against list_collapse2.\n# \n# However, I have discovered that these are \n# merely curious re-discoveries of unlist().\n\nlist_collapse3 \u003c- function (x, index) tapply(x, index, unlist)\n\nlist_collapse3(l, collapse_index1)\nlist_collapse3(l, collapse_index2)\n\n# Alternatively, could allow conditional input\n# rather than an index vector.\n```\n\n### Files\n\n* `find_files(files, steps = 1L, path = \".\")` = find a vector of file names; would be useful when looking for an `.Rproj` file to call home\n\n```r\n# Possible implementations\n\n# This function takes no file names as input\n# but it behaves similar to how find_files()\n# would behave.\n# Need to add a stop for the number of iterations.\nfind_rproj \u003c- function(wd = getwd(), it = 1L) {\n    found \u003c- length(list.files(wd, pattern = \"\\\\.Rproj\")) == 1\n    if (found) {  # Found it.\n        return(wd) \n    } else if (wd == \".\" | grepl(\"\\\\:/\", wd)) { # Stop if nothing found.\n        warning(\"Could not find Rproj file.\")\n        return(invisible(NULL))\n    } else if (it \u003e 5L ) {  # Stop before things get out of control.\n        warning(\"Could not find Rproj file.\")\n        return(invisible(NULL))\n    } else {\n        it \u003c- it + 1L\n        find_rproj(wd \u003c- dirname(wd)) # Keep going\n    }\n}\n```\n\n* `file_setdiff(x, y)` = path distance between two file paths\n    * possible spin-offs\n        * `file_intersect(x, y)` = path in common between two file paths (hmmm)\n        * `file_union(x, y)` = combined distinct file path (hmmm)\n\n```r\n# Expected behavior\n\nfile_setdiff(\"some/file/path/here\", \"some/file\")\n#\u003e [1] \"path/here\"\n\nfile_setdiff(\"some/file/path\", \"another/file/path\")\n#\u003e [1] character(0)\n```\n\n### Environments\n\n* `lock()` = lock object in current environment (taken from [here](https://colinfay.me/js-const-r/))\n\n```r\nlock \u003c- function(x){\n  lockBinding(\n    deparse(\n      substitute(x)), \n    env = parent.frame()\n  )\n}\n\nplop \u003c- 12\nlock(plop)\nplop \u003c- 13\n```\n\n___\n\n## New Behavior\n\n* `json.lock`/`.Gemfile` for package version control (when needed).\n* An R launcher similar to Python's Windows launcher.\n* `rep(x, ..., sort = FALSE)` = sort the repitions; who can remember `each`?\n* `dirname(path, steps = 1)` = how many steps to go back? \n    * `steps` should default to `1` (because that is the current behavior) and be limited by new option `max.steps` (default = `15`)\n* `list.files(path, recursive, direction = Inf)` = control the depth and direction of recursion\n    * allow files to be listed in both recursively forwards and backwards\n    * control depth of recursion (default would be `Inf`)\n* `setdiff(x = \"some/file/path/here\", y = \"some/file\")` = allow setdiffing on file paths\n    * would help determine path distance between two file paths\n    * would be difficult to implement -- prefer `file_setdiff()` instead\n\n___\n\n## Argument Order\n\nNew argument order of existing functions\n\n1. `x` should ALWAYS come first\n* `Filter(x, f)`\n* `Reduce(x, f, init, accumulate = FALSE, right = FALSE)`\n\n2. `pattern` should follow `x`; `ignore.case` and `fixed` are more relevant than `perl`; also, see # 1\n* `grep(x, pattern, ignore.case = FALSE, value = FALSE, fixed = FALSE, invert = FALSE, useBytes = FALSE, perl = FALSE)`\n* `grepl(x, pattern, ignore.case = FALSE, fixed = FALSE, useBytes = FALSE, perl = FALSE)`\n* `gsub(x, pattern, replacement, ignore.case = FALSE, fixed = FALSE, useBytes = FALSE, perl = FALSE)`\n\n___\n\n## Remove\n\nRemove a function from base R\n\n* `as.name()` --OR-- `as.symbol()` = they are the exact same thing (`identical(as.name, as.symbol) # [1] TRUE`); we only need one\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseasmith%2Fr-wishlist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseasmith%2Fr-wishlist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseasmith%2Fr-wishlist/lists"}