├── .Rbuildignore ├── .covrignore ├── .github ├── .gitignore ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── move.yml └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── conditions.R ├── doc-tidy-selection.R ├── eval-bool.R ├── eval-c.R ├── eval-relocate.R ├── eval-rename.R ├── eval-select.R ├── eval-walk.R ├── faq.R ├── helpers-misc.R ├── helpers-pattern.R ├── helpers-vector.R ├── helpers-where.R ├── helpers.R ├── import-standalone-obj-type.R ├── import-standalone-purrr.R ├── import-standalone-types-check.R ├── lifecycle-deprecated.R ├── proxy.R ├── reexport-rlang.R ├── sets.R ├── tidyselect-package.R ├── utils.R ├── vars-pull.R ├── vars.R └── zzz.R ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── man ├── all_of.Rd ├── args_tidy_select.Rd ├── eval_relocate.Rd ├── eval_select.Rd ├── everything.Rd ├── faq-external-vector.Rd ├── faq-selection-context.Rd ├── faq │ ├── external-vector.Rmd │ ├── selection-context.Rmd │ └── setup.Rmd ├── language.Rd ├── one_of.Rd ├── peek_vars.Rd ├── poke_vars.Rd ├── reexports.Rd ├── rmd │ ├── overview.Rmd │ └── setup.Rmd ├── starts_with.Rd ├── tidyselect-package.Rd ├── tidyselect_data_proxy.Rd ├── vars_pull.Rd ├── vars_select.Rd ├── vars_select_helpers.Rd └── where.Rd ├── revdep ├── .gitignore ├── README.md ├── cran.md ├── dplyr │ ├── README.md │ ├── failures.md │ └── problems.md ├── failures.md ├── problems.md └── tidyr │ ├── README.md │ ├── failures.md │ └── problems.md ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── eval-bool.md │ ├── eval-c.md │ ├── eval-relocate.md │ ├── eval-rename.md │ ├── eval-select.md │ ├── eval-walk.md │ ├── helpers-misc.md │ ├── helpers-pattern.md │ ├── helpers-vector.md │ ├── helpers-where.md │ ├── helpers.md │ ├── lifecycle-deprecated.md │ ├── proxy.md │ ├── vars-pull.md │ └── vars.md │ ├── helper-tidyselect.R │ ├── test-eval-bool.R │ ├── test-eval-c.R │ ├── test-eval-relocate.R │ ├── test-eval-rename.R │ ├── test-eval-select.R │ ├── test-eval-walk.R │ ├── test-helpers-misc.R │ ├── test-helpers-pattern.R │ ├── test-helpers-vector.R │ ├── test-helpers-where.R │ ├── test-helpers.R │ ├── test-lifecycle-deprecated.R │ ├── test-proxy.R │ ├── test-sets.R │ ├── test-vars-pull.R │ └── test-vars.R ├── tidyselect.Rproj └── vignettes ├── .gitignore ├── syntax.Rmd └── tidyselect.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | ^\.travis\.yml$ 5 | ^cran-comments\.md$ 6 | ^revdep$ 7 | ^revdep-all$ 8 | ^codecov\.yml$ 9 | ^\.github$ 10 | ^_pkgdown\.yml$ 11 | ^docs$ 12 | ^\.covrignore$ 13 | ^rsconnect$ 14 | ^LICENSE\.md$ 15 | ^CRAN-SUBMISSION$ 16 | -------------------------------------------------------------------------------- /.covrignore: -------------------------------------------------------------------------------- 1 | R/deprec-*.R 2 | R/compat-*.R 3 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS for tidyselect 2 | # https://www.tidyverse.org/development/understudies 3 | .github/CODEOWNERS @lionel- @hadley 4 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at codeofconduct@posit.co. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.1, available at 118 | . 119 | 120 | Community Impact Guidelines were inspired by 121 | [Mozilla's code of conduct enforcement ladder][https://github.com/mozilla/inclusion]. 122 | 123 | For answers to common questions about this code of conduct, see the FAQ at 124 | . Translations are available at . 125 | 126 | [homepage]: https://www.contributor-covenant.org 127 | -------------------------------------------------------------------------------- /.github/move.yml: -------------------------------------------------------------------------------- 1 | # Configuration for move-issues bot - https://github.com/dessant/move-issues 2 | 3 | # Delete the command comment when it contains no other content 4 | deleteCommand: true 5 | 6 | # Close the source issue after moving 7 | closeSourceIssue: true 8 | 9 | # Lock the source issue after moving 10 | lockSourceIssue: false 11 | 12 | # Mention issue and comment authors 13 | mentionAuthors: true 14 | 15 | # Preserve mentions in the issue content 16 | keepContentMentions: true 17 | 18 | # Set custom aliases for targets 19 | # aliases: 20 | # r: repo 21 | # or: owner/repo 22 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | 13 | name: R-CMD-check.yaml 14 | 15 | permissions: read-all 16 | 17 | jobs: 18 | R-CMD-check: 19 | runs-on: ${{ matrix.config.os }} 20 | 21 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | config: 27 | - {os: macos-latest, r: 'release'} 28 | 29 | - {os: windows-latest, r: 'release'} 30 | # use 4.0 or 4.1 to check with rtools40's older compiler 31 | - {os: windows-latest, r: 'oldrel-4'} 32 | 33 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 34 | - {os: ubuntu-latest, r: 'release'} 35 | - {os: ubuntu-latest, r: 'oldrel-1'} 36 | - {os: ubuntu-latest, r: 'oldrel-2'} 37 | - {os: ubuntu-latest, r: 'oldrel-3'} 38 | - {os: ubuntu-latest, r: 'oldrel-4'} 39 | 40 | env: 41 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 42 | R_KEEP_PKG_SOURCE: yes 43 | 44 | steps: 45 | - uses: actions/checkout@v4 46 | 47 | - uses: r-lib/actions/setup-pandoc@v2 48 | 49 | - uses: r-lib/actions/setup-r@v2 50 | with: 51 | r-version: ${{ matrix.config.r }} 52 | http-user-agent: ${{ matrix.config.http-user-agent }} 53 | use-public-rspm: true 54 | 55 | - uses: r-lib/actions/setup-r-dependencies@v2 56 | with: 57 | extra-packages: any::rcmdcheck 58 | needs: check 59 | 60 | - uses: r-lib/actions/check-r-package@v2 61 | with: 62 | upload-snapshots: true 63 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 64 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown.yaml 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | # Only restrict concurrency for non-PR jobs 20 | concurrency: 21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::. 38 | needs: website 39 | 40 | - name: Build site 41 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 42 | shell: Rscript {0} 43 | 44 | - name: Deploy to GitHub pages 🚀 45 | if: github.event_name != 'pull_request' 46 | uses: JamesIves/github-pages-deploy-action@v4.5.0 47 | with: 48 | clean: false 49 | branch: gh-pages 50 | folder: docs 51 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: pr-commands.yaml 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 14 | name: document 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | permissions: 19 | contents: write 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - uses: r-lib/actions/pr-fetch@v2 24 | with: 25 | repo-token: ${{ secrets.GITHUB_TOKEN }} 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | 31 | - uses: r-lib/actions/setup-r-dependencies@v2 32 | with: 33 | extra-packages: any::roxygen2 34 | needs: pr-document 35 | 36 | - name: Document 37 | run: roxygen2::roxygenise() 38 | shell: Rscript {0} 39 | 40 | - name: commit 41 | run: | 42 | git config --local user.name "$GITHUB_ACTOR" 43 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 44 | git add man/\* NAMESPACE 45 | git commit -m 'Document' 46 | 47 | - uses: r-lib/actions/pr-push@v2 48 | with: 49 | repo-token: ${{ secrets.GITHUB_TOKEN }} 50 | 51 | style: 52 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 53 | name: style 54 | runs-on: ubuntu-latest 55 | env: 56 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 57 | permissions: 58 | contents: write 59 | steps: 60 | - uses: actions/checkout@v4 61 | 62 | - uses: r-lib/actions/pr-fetch@v2 63 | with: 64 | repo-token: ${{ secrets.GITHUB_TOKEN }} 65 | 66 | - uses: r-lib/actions/setup-r@v2 67 | 68 | - name: Install dependencies 69 | run: install.packages("styler") 70 | shell: Rscript {0} 71 | 72 | - name: Style 73 | run: styler::style_pkg() 74 | shell: Rscript {0} 75 | 76 | - name: commit 77 | run: | 78 | git config --local user.name "$GITHUB_ACTOR" 79 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 80 | git add \*.R 81 | git commit -m 'Style' 82 | 83 | - uses: r-lib/actions/pr-push@v2 84 | with: 85 | repo-token: ${{ secrets.GITHUB_TOKEN }} 86 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Test coverage 32 | run: | 33 | cov <- covr::package_coverage( 34 | quiet = FALSE, 35 | clean = FALSE, 36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 37 | ) 38 | covr::to_cobertura(cov) 39 | shell: Rscript {0} 40 | 41 | - uses: codecov/codecov-action@v4 42 | with: 43 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} 44 | file: ./cobertura.xml 45 | plugin: noop 46 | disable_search: true 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | 49 | - name: Show testthat output 50 | if: always() 51 | run: | 52 | ## -------------------------------------------------------------------- 53 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 54 | shell: bash 55 | 56 | - name: Upload test results 57 | if: failure() 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: coverage-test-failures 61 | path: ${{ runner.temp }}/package 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | revdep/checks 5 | revdep/library 6 | *.o 7 | *.so 8 | issues 9 | docs/ 10 | inst/doc 11 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: tidyselect 2 | Title: Select from a Set of Strings 3 | Version: 1.2.1.9000 4 | Authors@R: c( 5 | person("Lionel", "Henry", , "lionel@posit.co", role = c("aut", "cre")), 6 | person("Hadley", "Wickham", , "hadley@posit.co", role = "aut"), 7 | person("Posit Software, PBC", role = c("cph", "fnd")) 8 | ) 9 | Description: A backend for the selecting functions of the 'tidyverse'. It 10 | makes it easy to implement select-like functions in your own packages 11 | in a way that is consistent with other 'tidyverse' interfaces for 12 | selection. 13 | License: MIT + file LICENSE 14 | URL: https://tidyselect.r-lib.org, https://github.com/r-lib/tidyselect 15 | BugReports: https://github.com/r-lib/tidyselect/issues 16 | Depends: 17 | R (>= 4.0) 18 | Imports: 19 | cli (>= 3.3.0), 20 | glue (>= 1.3.0), 21 | lifecycle (>= 1.0.3), 22 | rlang (>= 1.1.0), 23 | vctrs (>= 0.5.2), 24 | withr 25 | Suggests: 26 | covr, 27 | crayon, 28 | dplyr, 29 | knitr, 30 | magrittr, 31 | rmarkdown, 32 | stringr, 33 | testthat (>= 3.2.1), 34 | tibble (>= 2.1.3) 35 | VignetteBuilder: 36 | knitr 37 | ByteCompile: true 38 | Config/Needs/website: tidyverse/tidytemplate 39 | Config/testthat/edition: 3 40 | Encoding: UTF-8 41 | Roxygen: list(markdown = TRUE) 42 | RoxygenNote: 7.3.2 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2024 2 | COPYRIGHT HOLDER: tidyselect authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2024 tidyselect authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(tidyselect_data_has_predicates,default) 4 | S3method(tidyselect_data_proxy,default) 5 | export(all_of) 6 | export(any_of) 7 | export(contains) 8 | export(ends_with) 9 | export(enquo) 10 | export(eval_relocate) 11 | export(eval_rename) 12 | export(eval_select) 13 | export(everything) 14 | export(last_col) 15 | export(matches) 16 | export(num_range) 17 | export(one_of) 18 | export(peek_data) 19 | export(peek_vars) 20 | export(poke_vars) 21 | export(quo) 22 | export(quo_name) 23 | export(quos) 24 | export(scoped_vars) 25 | export(starts_with) 26 | export(tidyselect_data_has_predicates) 27 | export(tidyselect_data_proxy) 28 | export(vars_pull) 29 | export(vars_rename) 30 | export(vars_select) 31 | export(vars_select_helpers) 32 | export(where) 33 | export(with_vars) 34 | import(rlang) 35 | importFrom(glue,glue) 36 | importFrom(rlang,enquo) 37 | importFrom(rlang,quo) 38 | importFrom(rlang,quo_name) 39 | importFrom(rlang,quos) 40 | -------------------------------------------------------------------------------- /R/conditions.R: -------------------------------------------------------------------------------- 1 | with_subscript_errors <- function(expr, type = "select") { 2 | withCallingHandlers( 3 | expr, 4 | vctrs_error_subscript = function(cnd) { 5 | cnd$subscript_action <- subscript_action(type) 6 | cnd$subscript_elt <- "column" 7 | cnd_signal(cnd) 8 | } 9 | ) 10 | } 11 | 12 | with_chained_errors <- function(expr, action, call, eval_expr = NULL) { 13 | withCallingHandlers( 14 | expr, 15 | error = function(cnd) { 16 | eval_expr <- quo_squash(eval_expr) 17 | # Only display a message if there's useful context to add 18 | if (!is_call(eval_expr) || identical(cnd[["call"]], call2(eval_expr[[1]])) ) { 19 | msg <- "" 20 | } else { 21 | code <- as_label(eval_expr) 22 | msg <- cli::format_inline("In argument: {.code {code}}.") 23 | } 24 | cli::cli_abort(c("i" = msg), call = call, parent = cnd) 25 | } 26 | ) 27 | } 28 | 29 | subscript_action <- function(type) { 30 | switch(validate_type(type), 31 | select = "select", 32 | rename = "rename", 33 | relocate = "relocate", 34 | pull = "extract" 35 | ) 36 | } 37 | validate_type <- function(type) { 38 | # We might add `recode` in the future 39 | if (!is_string(type, c("select", "rename", "relocate", "pull"))) { 40 | cli::cli_abort("Unexpected value for {.arg tidyselect_type}.", .internal = TRUE) 41 | } 42 | type 43 | } 44 | -------------------------------------------------------------------------------- /R/doc-tidy-selection.R: -------------------------------------------------------------------------------- 1 | #' Argument type: tidy-select 2 | #' 3 | #' @description 4 | #' This page describes the `` argument modifier which indicates 5 | #' the argument supports **tidy selections**. Tidy selection provides a concise 6 | #' dialect of R for selecting variables based on their names or properties. 7 | #' 8 | #' Tidy selection is a variant of tidy evaluation. This means that inside 9 | #' functions tidy-select arguments require special attention, as described in 10 | #' the *Indirection* section below. If you've never heard of tidy evaluation 11 | #' before, start with `vignette("programming")`. 12 | #' 13 | #' 14 | #' # Overview of selection features 15 | #' 16 | #' ```{r, child = "man/rmd/overview.Rmd"} 17 | #' ``` 18 | #' 19 | #' 20 | #' # Indirection 21 | #' 22 | #' There are two main cases: 23 | #' 24 | #' * If you want the user to supply a character vector of column names, use `all_of()` 25 | #' or `any_of()`, depending on whether or not you want unknown variable 26 | #' names to cause an error, e.g. `select(df, all_of(vars))`, 27 | #' `select(df, !any_of(vars))`. 28 | #' 29 | #' * If you want the user to supply a tidyselect specification in 30 | #' a function argument, embrace the function argument, e.g. 31 | #' `select(df, {{ vars }})`. 32 | #' 33 | #' @keywords internal 34 | #' @name args_tidy_select 35 | NULL 36 | -------------------------------------------------------------------------------- /R/eval-bool.R: -------------------------------------------------------------------------------- 1 | 2 | eval_bang <- function(expr, data_mask, context_mask) { 3 | x <- walk_data_tree(expr[[2]], data_mask, context_mask) 4 | 5 | vars <- data_mask$.__tidyselect__.$internal$vars 6 | error_call <- mask_error_call(data_mask) 7 | sel_complement(x, vars, error_call = error_call) 8 | } 9 | 10 | eval_or <- function(expr, data_mask, context_mask) { 11 | x <- walk_operand(expr[[2]], data_mask, context_mask) 12 | y <- walk_operand(expr[[3]], data_mask, context_mask) 13 | 14 | sel_union(x, y) 15 | } 16 | 17 | eval_and <- function(expr, data_mask, context_mask) { 18 | x <- expr[[2]] 19 | y <- expr[[3]] 20 | 21 | if (is_symbol(x) && is_symbol(y)) { 22 | x_name <- as_string(x) 23 | y_name <- as_string(y) 24 | 25 | x <- eval_sym(x, data_mask, context_mask, strict = TRUE) 26 | y <- eval_sym(y, data_mask, context_mask, strict = TRUE) 27 | 28 | if (!is_function(x) && !is_function(y)) { 29 | cli::cli_abort( 30 | c( 31 | "Can't take the intersection of two columns.", 32 | # can't use {.code}: https://github.com/r-lib/cli/issues/422 33 | i = "`{x_name} & {y_name}` is always an empty selection." 34 | ), 35 | call = mask_error_call(data_mask) 36 | ) 37 | } 38 | } 39 | 40 | x <- walk_operand(x, data_mask, context_mask) 41 | y <- walk_operand(y, data_mask, context_mask) 42 | 43 | sel_intersect(x, y) 44 | } 45 | 46 | walk_operand <- function(expr, data_mask, context_mask) { 47 | if (is_symbol(expr)) { 48 | expr <- eval_sym(expr, data_mask, context_mask, strict = TRUE) 49 | } 50 | walk_data_tree(expr, data_mask, context_mask) 51 | } 52 | 53 | stop_bad_bool_op <- function(bad, ok, call) { 54 | cli::cli_abort( 55 | c( 56 | "Can't use scalar {.code {bad}} in selections.", 57 | i = "Do you need {.arg {ok}} instead?" 58 | ), 59 | call = call 60 | ) 61 | } 62 | 63 | stop_bad_arith_op <- function(op, call) { 64 | cli::cli_abort( 65 | "Can't use arithmetic operator `{op}` in selection context.", 66 | call = call 67 | ) 68 | } 69 | 70 | stop_formula <- function(expr, call) { 71 | f <- as_label(expr) 72 | cli::cli_abort( 73 | c( 74 | "Formula shorthand must be wrapped in `where()`.", 75 | "", 76 | " " = " # Bad", 77 | " " = " data %>% select({f})", 78 | "", 79 | " " = " # Good", 80 | " " = " data %>% select(where({f}))" 81 | ), 82 | call = call 83 | ) 84 | } 85 | -------------------------------------------------------------------------------- /R/eval-c.R: -------------------------------------------------------------------------------- 1 | 2 | eval_c <- function(expr, data_mask, context_mask) { 3 | expr <- call_expand_dots(expr, context_mask$.__current__.) 4 | expr <- node_compact_missing(expr) 5 | node <- node_cdr(expr) 6 | 7 | # If the first selector is exclusive (negative), start with all 8 | # columns. `-foo` is syntax for `everything() - foo`. 9 | if (c_arg_kind(node_car(node)) %in% c("diff", "diff_colon")) { 10 | init <- quote(everything()) 11 | } else { 12 | init <- named(int()) 13 | } 14 | 15 | reduce_sels(node, data_mask, context_mask, init = init) 16 | } 17 | 18 | reduce_sels <- function(node, data_mask, context_mask, init) { 19 | out <- walk_data_tree(init, data_mask, context_mask) 20 | 21 | while (!is_null(node)) { 22 | tag <- node_tag(node) 23 | car <- node_car(node) 24 | cdr <- node_cdr(node) 25 | 26 | kind <- c_arg_kind(car) 27 | new <- switch(kind, 28 | diff = unnegate(car), 29 | diff_colon = unnegate_colon(car), 30 | car 31 | ) 32 | 33 | new <- walk_data_tree(new, data_mask, context_mask) 34 | if (!is_null(tag)) { 35 | internal <- data_mask$.__tidyselect__.$internal 36 | new <- combine_names( 37 | new, 38 | tag, 39 | internal$name_spec, 40 | internal$strict, 41 | error_call = internal$error_call 42 | ) 43 | } 44 | 45 | if (kind == "union") { 46 | out <- sel_union(out, new) 47 | } else { 48 | vars <- data_mask$.__tidyselect__.$internal$vars 49 | error_call <- mask_error_call(data_mask) 50 | out <- sel_diff(out, new, vars, error_call = error_call) 51 | } 52 | 53 | node <- cdr 54 | } 55 | 56 | out 57 | } 58 | 59 | c_arg_kind <- function(x) { 60 | expr <- quo_get_expr2(x, x) 61 | 62 | if (is_negated(x)) { 63 | "diff" 64 | } else if (is_negated_colon(x)) { 65 | "diff_colon" 66 | } else { 67 | "union" 68 | } 69 | } 70 | 71 | unnegate <- function(x) { 72 | expr <- quo_get_expr2(x, x) 73 | expr <- node_cadr(expr) 74 | 75 | if (is_quosure(expr)) { 76 | expr 77 | } else if (is_quosure(x)) { 78 | quo_set_expr(x, expr) 79 | } else { 80 | expr 81 | } 82 | } 83 | unnegate_colon <- function(x) { 84 | expr <- quo_get_expr2(x, x) 85 | 86 | expr[[2]] <- unnegate(expr[[2]]) 87 | expr[[3]] <- unnegate(expr[[3]]) 88 | 89 | quo_set_expr2(x, expr, expr) 90 | } 91 | 92 | is_negated <- function(x) { 93 | expr <- quo_get_expr2(x, x) 94 | is_call(expr, "-", n = 1) 95 | } 96 | is_negated_colon <- function(x) { 97 | expr <- quo_get_expr2(x, x) 98 | is_call(expr, ":") && is_negated(expr[[2]]) && is_negated(expr[[3]]) 99 | } 100 | 101 | combine_names <- function(x, 102 | tag, 103 | name_spec, 104 | uniquely_named, 105 | error_call) { 106 | if (uniquely_named && is_data_dups(x)) { 107 | name <- as_string(tag) 108 | cli::cli_abort("Can't rename duplicate variables to `{name}`.", call = error_call) 109 | } 110 | 111 | vctrs::vec_c(!!tag := x, .name_spec = name_spec) 112 | } 113 | unique_name_spec <- function(outer, inner) { 114 | # For compatibility, we enumerate as "foo1", "foo2", rather than 115 | # "foo...1", "foo...2" 116 | sep <- if (is_character(inner)) "..." else "" 117 | paste(outer, inner, sep = sep) 118 | } 119 | minimal_name_spec <- function(outer, inner) { 120 | if (is_character(inner)) { 121 | paste(outer, inner, sep = "...") 122 | } else { 123 | outer 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /R/eval-relocate.R: -------------------------------------------------------------------------------- 1 | #' Evaluate an expression to relocate variables 2 | #' 3 | #' @description 4 | #' `eval_relocate()` is a variant of [eval_select()] that moves a selection to 5 | #' a new location. Either `before` or `after` can be provided to specify where 6 | #' to move the selection to. This powers `dplyr::relocate()`. 7 | #' 8 | #' @inheritParams eval_select 9 | #' 10 | #' @param before,after Defused R code describing a selection according to the 11 | #' tidyselect syntax. The selection represents the destination of the 12 | #' selection provided through `expr`. Supplying neither of these will move the 13 | #' selection to the left-hand side. Supplying both of these is an error. 14 | #' 15 | #' @param before_arg,after_arg Argument names for `before` and `after`. These 16 | #' are used in error messages. 17 | #' 18 | #' @return 19 | #' A named vector of numeric locations with length equal to `length(data)`. 20 | #' Each position in `data` will be represented exactly once. 21 | #' 22 | #' The names are normally the same as in the input data, except when the user 23 | #' supplied named selections with `c()`. In the latter case, the names reflect 24 | #' the new names chosen by the user. 25 | #' 26 | #' @export 27 | #' @examples 28 | #' library(rlang) 29 | #' 30 | #' # Interpret defused code as a request to relocate 31 | #' x <- expr(c(mpg, disp)) 32 | #' after <- expr(wt) 33 | #' eval_relocate(x, mtcars, after = after) 34 | #' 35 | #' # Supplying neither `before` nor `after` will move the selection to the 36 | #' # left-hand side 37 | #' eval_relocate(x, mtcars) 38 | #' 39 | #' # Within a function, use `enquo()` to defuse a single argument. 40 | #' # Note that `before` and `after` must also be defused with `enquo()`. 41 | #' my_relocator <- function(x, expr, before = NULL, after = NULL) { 42 | #' eval_relocate(enquo(expr), x, before = enquo(before), after = enquo(after)) 43 | #' } 44 | #' 45 | #' my_relocator(mtcars, vs, before = hp) 46 | #' 47 | #' # Here is an example of using `eval_relocate()` to implement `relocate()`. 48 | #' # Note that the dots are passed on as a defused call to `c(...)`. 49 | #' relocate <- function(.x, ..., .before = NULL, .after = NULL) { 50 | #' pos <- eval_relocate( 51 | #' expr(c(...)), 52 | #' .x, 53 | #' before = enquo(.before), 54 | #' after = enquo(.after) 55 | #' ) 56 | #' set_names(.x[pos], names(pos)) 57 | #' } 58 | #' 59 | #' relocate(mtcars, vs, .before = hp) 60 | #' relocate(mtcars, starts_with("d"), .after = last_col()) 61 | eval_relocate <- function(expr, 62 | data, 63 | ..., 64 | before = NULL, 65 | after = NULL, 66 | strict = TRUE, 67 | name_spec = NULL, 68 | allow_rename = TRUE, 69 | allow_empty = TRUE, 70 | allow_predicates = TRUE, 71 | before_arg = "before", 72 | after_arg = "after", 73 | env = caller_env(), 74 | error_arg = NULL, 75 | error_call = caller_env()) { 76 | check_dots_empty() 77 | 78 | allow_predicates <- allow_predicates && tidyselect_data_has_predicates(data) 79 | data <- tidyselect_data_proxy(data) 80 | 81 | expr <- as_quosure(expr, env = env) 82 | 83 | sel <- eval_select_impl( 84 | x = data, 85 | names = names(data), 86 | expr = expr, 87 | strict = strict, 88 | name_spec = name_spec, 89 | allow_rename = allow_rename, 90 | allow_empty = allow_empty, 91 | allow_predicates = allow_predicates, 92 | type = "relocate", 93 | error_arg = error_arg, 94 | error_call = error_call 95 | ) 96 | 97 | # Enforce the invariant that relocating can't change the number of columns by 98 | # retaining only the last instance of a column that is renamed multiple times 99 | # TODO: https://github.com/r-lib/vctrs/issues/1442 100 | # `sel <- vctrs::vec_unique(sel, which = "last")` 101 | loc_last <- which(!duplicated(sel, fromLast = TRUE)) 102 | sel <- vctrs::vec_slice(sel, loc_last) 103 | 104 | n <- length(data) 105 | 106 | before <- as_quosure(before, env = env) 107 | after <- as_quosure(after, env = env) 108 | 109 | has_before <- !quo_is_null(before) 110 | has_after <- !quo_is_null(after) 111 | 112 | if (has_before && has_after) { 113 | cli::cli_abort( 114 | "Can't supply both {.arg {before_arg}} and {.arg {after_arg}}.", 115 | call = error_call 116 | ) 117 | } 118 | 119 | if (has_before) { 120 | where <- eval_select( 121 | expr = before, 122 | data = data, 123 | env = env, 124 | error_call = error_call, 125 | allow_predicates = allow_predicates, 126 | allow_rename = FALSE, 127 | error_arg = before_arg 128 | ) 129 | where <- unname(where) 130 | 131 | if (length(where) == 0L) { 132 | # Empty `before` selection pushes `sel` to the front 133 | where <- 1L 134 | } else { 135 | where <- min(where) 136 | } 137 | } else if (has_after) { 138 | where <- eval_select( 139 | expr = after, 140 | data = data, 141 | env = env, 142 | error_call = error_call, 143 | allow_predicates = allow_predicates, 144 | allow_rename = FALSE, 145 | error_arg = after_arg 146 | ) 147 | where <- unname(where) 148 | 149 | if (length(where) == 0L) { 150 | # Empty `after` selection pushes `sel` to the back 151 | where <- n 152 | } else { 153 | where <- max(where) 154 | } 155 | 156 | where <- where + 1L 157 | } else { 158 | # Defaults to `before = everything()` if neither 159 | # `before` nor `after` are supplied 160 | where <- 1L 161 | } 162 | 163 | lhs <- seq2(1L, where - 1L) 164 | rhs <- seq2(where, n) 165 | 166 | lhs <- setdiff(lhs, sel) 167 | rhs <- setdiff(rhs, sel) 168 | 169 | names <- names(data) 170 | 171 | names(lhs) <- names[lhs] 172 | names(rhs) <- names[rhs] 173 | 174 | sel <- vctrs::vec_c(lhs, sel, rhs) 175 | 176 | sel 177 | } 178 | -------------------------------------------------------------------------------- /R/eval-rename.R: -------------------------------------------------------------------------------- 1 | #' @rdname eval_select 2 | #' @export 3 | eval_rename <- function(expr, 4 | data, 5 | env = caller_env(), 6 | ..., 7 | strict = TRUE, 8 | name_spec = NULL, 9 | allow_predicates = TRUE, 10 | error_call = caller_env()) { 11 | check_dots_empty() 12 | 13 | allow_predicates <- allow_predicates && tidyselect_data_has_predicates(data) 14 | data <- tidyselect_data_proxy(data) 15 | 16 | rename_impl( 17 | data, 18 | names(data), 19 | as_quosure(expr, env), 20 | strict = strict, 21 | name_spec = name_spec, 22 | allow_predicates = allow_predicates, 23 | error_call = error_call 24 | ) 25 | } 26 | 27 | # Caller must put vars in scope 28 | rename_impl <- function(x, 29 | names, 30 | sel, 31 | strict = TRUE, 32 | name_spec = NULL, 33 | allow_predicates = TRUE, 34 | error_call) { 35 | if (is_null(names)) { 36 | cli::cli_abort("Can't rename an unnamed vector.", call = error_call) 37 | } 38 | 39 | pos <- eval_select_impl( 40 | x, 41 | names, 42 | {{ sel }}, 43 | strict = strict, 44 | name_spec = name_spec, 45 | type = "rename", 46 | allow_predicates = allow_predicates, 47 | error_call = error_call 48 | ) 49 | 50 | # Check for unique names only if input is a data frame 51 | if (is.data.frame(x) || is_null(x)) { 52 | names[pos] <- names(pos) 53 | with_subscript_errors( 54 | vctrs::vec_as_names( 55 | names, 56 | repair = "check_unique", 57 | call = error_call 58 | ) 59 | ) 60 | } 61 | 62 | pos 63 | } 64 | 65 | # Example implementation mainly used for unit tests 66 | rename <- function(.x, ..., .strict = TRUE) { 67 | pos <- eval_rename(expr(c(...)), .x, strict = .strict) 68 | names(.x)[pos] <- names(pos) 69 | .x 70 | } 71 | -------------------------------------------------------------------------------- /R/faq.R: -------------------------------------------------------------------------------- 1 | #' FAQ - Error: Must be used within a *selecting* function 2 | #' 3 | #' @includeRmd man/faq/selection-context.Rmd description 4 | #' 5 | #' @name faq-selection-context 6 | NULL 7 | 8 | #' FAQ - Note: Using an external vector in selections is ambiguous 9 | #' 10 | #' @includeRmd man/faq/external-vector.Rmd description 11 | #' 12 | #' @name faq-external-vector 13 | NULL 14 | -------------------------------------------------------------------------------- /R/helpers-misc.R: -------------------------------------------------------------------------------- 1 | #' Select all variables or the last variable 2 | #' 3 | #' @description 4 | #' 5 | #' These functions are [selection helpers][language]. 6 | #' 7 | #' * [everything()] selects all variable. It is also useful in 8 | #' combination with other tidyselect operators. 9 | #' 10 | #' * [last_col()] selects the last variable. 11 | #' 12 | #' @inheritParams starts_with 13 | #' 14 | #' @section Examples: 15 | #' 16 | #' ```{r, child = "man/rmd/setup.Rmd"} 17 | #' ``` 18 | #' 19 | #' Selection helpers can be used in functions like `dplyr::select()` 20 | #' or `tidyr::pivot_longer()`. Let's first attach the tidyverse: 21 | #' 22 | #' ```{r, comment = "#>", collapse = TRUE} 23 | #' library(tidyverse) 24 | #' 25 | #' # For better printing 26 | #' iris <- as_tibble(iris) 27 | #' mtcars <- as_tibble(mtcars) 28 | #' ``` 29 | #' 30 | #' Use `everything()` to select all variables: 31 | #' 32 | #' ```{r, comment = "#>", collapse = TRUE} 33 | #' iris %>% select(everything()) 34 | #' 35 | #' mtcars %>% pivot_longer(everything()) 36 | #' ``` 37 | #' 38 | #' Use `last_col()` to select the last variable: 39 | #' 40 | #' ```{r, comment = "#>", collapse = TRUE} 41 | #' iris %>% select(last_col()) 42 | #' 43 | #' mtcars %>% pivot_longer(last_col()) 44 | #' ``` 45 | #' 46 | #' Supply an offset `n` to select a variable located `n` positions 47 | #' from the end: 48 | #' 49 | #' ```{r, comment = "#>", collapse = TRUE} 50 | #' mtcars %>% select(1:last_col(5)) 51 | #' ``` 52 | #' 53 | #' @seealso `r rd_helpers_seealso()` 54 | #' @export 55 | everything <- function(vars = NULL) { 56 | vars <- vars %||% peek_vars(fn = "everything") 57 | seq_along(vars) 58 | } 59 | 60 | #' @rdname everything 61 | #' @export 62 | #' @param offset Set it to `n` to select the nth var from the end. 63 | last_col <- function(offset = 0L, vars = NULL) { 64 | if (!is_integerish(offset, n = 1)) { 65 | not <- obj_type_friendly(offset) 66 | cli::cli_abort("{.arg offset} must be a single integer, not {not}.") 67 | } 68 | 69 | vars <- vars %||% peek_vars(fn = "last_col") 70 | n <- length(vars) 71 | 72 | if (offset && n <= offset) { 73 | cli::cli_abort("{.arg offset} ({offset}) must be smaller than the number of columns ({n}).") 74 | } else if (n == 0) { 75 | cli::cli_abort("Can't select last column when input is empty.") 76 | } else { 77 | n - as.integer(offset) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /R/helpers-vector.R: -------------------------------------------------------------------------------- 1 | #' Select variables from character vectors 2 | #' 3 | #' @description 4 | #' 5 | #' These [selection helpers][language] select variables 6 | #' contained in a character vector. They are especially useful for 7 | #' programming with selecting functions. 8 | #' 9 | #' * [all_of()] is for strict selection. If any of the variables in 10 | #' the character vector is missing, an error is thrown. 11 | #' 12 | #' * [any_of()] doesn't check for missing variables. It is especially 13 | #' useful with negative selections, when you would like to make sure 14 | #' a variable is removed. 15 | #' 16 | #' The order of selected columns is determined by the order in the 17 | #' vector. 18 | #' 19 | #' @inheritParams starts_with 20 | #' @param x A vector of character names or numeric locations. 21 | #' 22 | #' @section Examples: 23 | #' 24 | #' ```{r, child = "man/rmd/setup.Rmd"} 25 | #' ``` 26 | #' 27 | #' Selection helpers can be used in functions like `dplyr::select()` 28 | #' or `tidyr::pivot_longer()`. Let's first attach the tidyverse: 29 | #' 30 | #' ```{r, comment = "#>", collapse = TRUE} 31 | #' library(tidyverse) 32 | #' 33 | #' # For better printing 34 | #' iris <- as_tibble(iris) 35 | #' ``` 36 | #' 37 | #' It is a common to have a names of variables in a vector. 38 | #' 39 | #' ```{r, comment = "#>", collapse = TRUE} 40 | #' vars <- c("Sepal.Length", "Sepal.Width") 41 | #' 42 | #' iris[, vars] 43 | #' ``` 44 | #' 45 | #' To refer to these variables in selecting function, use `all_of()`: 46 | #' 47 | #' ```{r, comment = "#>", collapse = TRUE} 48 | #' iris %>% select(all_of(vars)) 49 | #' 50 | #' iris %>% pivot_longer(all_of(vars)) 51 | #' ``` 52 | #' 53 | #' If any of these variables are missing from the data frame, that's an error: 54 | #' 55 | #' ```{r, error = TRUE} 56 | #' starwars %>% select(all_of(vars)) 57 | #' ``` 58 | #' 59 | #' Use `any_of()` to allow missing variables: 60 | #' 61 | #' ```{r, comment = "#>", collapse = TRUE} 62 | #' starwars %>% select(any_of(vars)) 63 | #' ``` 64 | #' 65 | #' `any_of()` is especially useful to remove variables from a data 66 | #' frame because calling it again does not cause an error: 67 | #' 68 | #' ```{r, comment = "#>", collapse = TRUE} 69 | #' iris %>% select(-any_of(vars)) 70 | #' 71 | #' iris %>% select(-any_of(vars)) %>% select(-any_of(vars)) 72 | #' ``` 73 | #' 74 | #' Supply named vectors to `all_of()` and `any_of()` to select and rename 75 | #' columns at the same time: 76 | #' 77 | #' ```{r, comment = "#>", collapse = TRUE} 78 | #' colors <- c(color_of_hair = "hair_color", color_of_eyes = "eye_color") 79 | #' starwars %>% select(all_of(colors)) 80 | #' ``` 81 | #' 82 | #' @seealso `r rd_helpers_seealso()` 83 | #' @export 84 | all_of <- function(x) { 85 | if (!has_vars()) { 86 | lifecycle::deprecate_soft( 87 | "1.2.0", 88 | I("Using `all_of()` outside of a selecting function"), 89 | details = paste("See details at", peek_vars_link()) 90 | ) 91 | return(x) 92 | } 93 | 94 | vars <- peek_vars(fn = "all_of") 95 | as_indices_impl(x, vars = vars, strict = TRUE) 96 | } 97 | 98 | #' @rdname all_of 99 | #' @inheritParams rlang::args_dots_empty 100 | #' @export 101 | any_of <- function(x, ..., vars = NULL) { 102 | vars <- vars %||% peek_vars(fn = "any_of") 103 | if (!missing(...)) { 104 | cli::cli_abort(c( 105 | "{.arg ...} must be empty.", 106 | i = "Did you forget {.code c()}?", 107 | i = 'The expected syntax is {.code any_of(c("a", "b"))}, not {.code any_of("a", "b")}' 108 | )) 109 | } 110 | as_indices_impl(x, vars = vars, strict = FALSE) 111 | } 112 | -------------------------------------------------------------------------------- /R/helpers-where.R: -------------------------------------------------------------------------------- 1 | #' Select variables with a function 2 | #' 3 | #' This [selection helper][language] selects the variables for which a 4 | #' function returns `TRUE`. 5 | #' 6 | #' @param fn A function that returns `TRUE` or `FALSE` (technically, a 7 | #' _predicate_ function). Can also be a purrr-like formula. 8 | #' 9 | #' @section Examples: 10 | #' 11 | #' ```{r, child = "man/rmd/setup.Rmd"} 12 | #' ``` 13 | #' 14 | #' Selection helpers can be used in functions like `dplyr::select()` 15 | #' or `tidyr::pivot_longer()`. Let's first attach the tidyverse: 16 | #' 17 | #' ```{r, comment = "#>", collapse = TRUE} 18 | #' library(tidyverse) 19 | #' 20 | #' # For better printing 21 | #' iris <- as_tibble(iris) 22 | #' ``` 23 | #' 24 | #' `where()` takes a function and returns all variables for which the 25 | #' function returns `TRUE`: 26 | #' 27 | #' ```{r, comment = "#>", collapse = TRUE} 28 | #' is.factor(iris[[4]]) 29 | #' 30 | #' is.factor(iris[[5]]) 31 | #' 32 | #' iris %>% select(where(is.factor)) 33 | #' 34 | #' is.numeric(iris[[4]]) 35 | #' 36 | #' is.numeric(iris[[5]]) 37 | #' 38 | #' iris %>% select(where(is.numeric)) 39 | #' ``` 40 | #' 41 | #' 42 | #' ## The formula shorthand 43 | #' 44 | #' You can use purrr-like formulas as a shortcut for creating a 45 | #' function on the spot. These expressions are equivalent: 46 | #' 47 | #' ```{r, comment = "#>", collapse = TRUE} 48 | #' iris %>% select(where(is.numeric)) 49 | #' 50 | #' iris %>% select(where(function(x) is.numeric(x))) 51 | #' 52 | #' iris %>% select(where(~ is.numeric(.x))) 53 | #' ``` 54 | #' 55 | #' The shorthand is useful for adding logic inline. Here we select all 56 | #' numeric variables whose mean is greater than 3.5: 57 | #' 58 | #' ```{r, comment = "#>", collapse = TRUE} 59 | #' iris %>% select(where(~ is.numeric(.x) && mean(.x) > 3.5)) 60 | #' ``` 61 | #' 62 | #' @export 63 | where <- function(fn) { 64 | predicate <- as_function(fn) 65 | call <- current_call() 66 | 67 | function(x, ...) { 68 | out <- predicate(x, ...) 69 | check_predicate_output(out, call = call) 70 | 71 | out 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /R/helpers.R: -------------------------------------------------------------------------------- 1 | #' Selection language 2 | #' 3 | #' @description 4 | #' 5 | #' ## Overview of selection features: 6 | #' 7 | #' ```{r, child = identity("man/rmd/overview.Rmd")} 8 | #' ``` 9 | #' 10 | #' @section Simple examples: 11 | #' 12 | #' ```{r, child = "man/rmd/setup.Rmd"} 13 | #' ``` 14 | #' 15 | #' Here we show the usage for the basic selection operators. See the 16 | #' specific help pages to learn about helpers like [starts_with()]. 17 | #' 18 | #' The selection language can be used in functions like 19 | #' `dplyr::select()` or `tidyr::pivot_longer()`. Let's first attach 20 | #' the tidyverse: 21 | #' 22 | #' ```{r, comment = "#>", collapse = TRUE} 23 | #' library(tidyverse) 24 | #' 25 | #' # For better printing 26 | #' iris <- as_tibble(iris) 27 | #' ``` 28 | #' 29 | #' Select variables by name: 30 | #' 31 | #' ```{r, comment = "#>", collapse = TRUE} 32 | #' starwars %>% select(height) 33 | #' 34 | #' iris %>% pivot_longer(Sepal.Length) 35 | #' ``` 36 | #' 37 | #' Select multiple variables by separating them with commas. Note how 38 | #' the order of columns is determined by the order of inputs: 39 | #' 40 | #' ```{r, comment = "#>", collapse = TRUE} 41 | #' starwars %>% select(homeworld, height, mass) 42 | #' ``` 43 | #' 44 | #' Functions like `tidyr::pivot_longer()` don't take variables with 45 | #' dots. In this case use `c()` to select multiple variables: 46 | #' 47 | #' ```{r, comment = "#>", collapse = TRUE} 48 | #' iris %>% pivot_longer(c(Sepal.Length, Petal.Length)) 49 | #' ``` 50 | #' 51 | #' ## Operators: 52 | #' 53 | #' The `:` operator selects a range of consecutive variables: 54 | #' 55 | #' ```{r, comment = "#>", collapse = TRUE} 56 | #' starwars %>% select(name:mass) 57 | #' ``` 58 | #' 59 | #' The `!` operator negates a selection: 60 | #' 61 | #' ```{r, comment = "#>", collapse = TRUE} 62 | #' starwars %>% select(!(name:mass)) 63 | #' 64 | #' iris %>% select(!c(Sepal.Length, Petal.Length)) 65 | #' 66 | #' iris %>% select(!ends_with("Width")) 67 | #' ``` 68 | #' 69 | #' `&` and `|` take the intersection or the union of two selections: 70 | #' 71 | #' ```{r, comment = "#>", collapse = TRUE} 72 | #' iris %>% select(starts_with("Petal") & ends_with("Width")) 73 | #' 74 | #' iris %>% select(starts_with("Petal") | ends_with("Width")) 75 | #' ``` 76 | #' 77 | #' To take the difference between two selections, combine the `&` and 78 | #' `!` operators: 79 | #' 80 | #' ```{r, comment = "#>", collapse = TRUE} 81 | #' iris %>% select(starts_with("Petal") & !ends_with("Width")) 82 | #' ``` 83 | #' 84 | #' @section Details: 85 | #' The order of selected columns is determined by the inputs. 86 | #' 87 | #' * `all_of(c("foo", "bar"))` selects `"foo"` first. 88 | #' 89 | #' * `c(starts_with("c"), starts_with("d"))` selects all columns 90 | #' starting with `"c"` first, then all columns starting with `"d"`. 91 | #' 92 | #' @name language 93 | #' @aliases select_helpers 94 | NULL 95 | 96 | 97 | #' Superseded selection helpers 98 | #' 99 | #' `one_of()` is superseded in favour of the more precise [any_of()] and 100 | #' [all_of()] selectors. 101 | #' 102 | #' @param ... One or more character vectors. 103 | #' @param .vars A character vector of variable names. When called 104 | #' from inside selecting functions like [dplyr::select()] these are 105 | #' automatically set to the names of the table. 106 | #' 107 | #' @keywords internal 108 | #' @export 109 | one_of <- function(..., .vars = NULL) { 110 | .vars <- .vars %||% peek_vars(fn = "one_of") 111 | keep <- compact(list(...)) 112 | 113 | bad_input <- detect_index(keep, ~ !vec_is_coercible(., chr())) 114 | if (bad_input) { 115 | type <- obj_type_friendly(keep[[bad_input]]) 116 | cli::cli_abort( 117 | "Input {bad_input} must be a vector of column names, not {type}.", 118 | class = "vctrs_error_incompatible_index_type" 119 | ) 120 | } 121 | 122 | keep <- vctrs::vec_c(!!!keep, .ptype = character()) 123 | 124 | if (!all(keep %in% .vars)) { 125 | bad <- setdiff(keep, .vars) 126 | warn(glue("Unknown columns: ", paste0("`", bad, "`", collapse = ", "))) 127 | } 128 | 129 | match_vars(keep, .vars) 130 | } 131 | 132 | rd_helpers_seealso <- function() { 133 | "The [selection language][language] page, which includes links to other selection helpers." 134 | } 135 | -------------------------------------------------------------------------------- /R/import-standalone-purrr.R: -------------------------------------------------------------------------------- 1 | # Standalone file: do not edit by hand 2 | # Source: https://github.com/r-lib/rlang/blob/HEAD/R/standalone-purrr.R 3 | # Generated by: usethis::use_standalone("r-lib/rlang", "purrr") 4 | # ---------------------------------------------------------------------- 5 | # 6 | # --- 7 | # repo: r-lib/rlang 8 | # file: standalone-purrr.R 9 | # last-updated: 2023-02-23 10 | # license: https://unlicense.org 11 | # imports: rlang 12 | # --- 13 | # 14 | # This file provides a minimal shim to provide a purrr-like API on top of 15 | # base R functions. They are not drop-in replacements but allow a similar style 16 | # of programming. 17 | # 18 | # ## Changelog 19 | # 20 | # 2023-02-23: 21 | # * Added `list_c()` 22 | # 23 | # 2022-06-07: 24 | # * `transpose()` is now more consistent with purrr when inner names 25 | # are not congruent (#1346). 26 | # 27 | # 2021-12-15: 28 | # * `transpose()` now supports empty lists. 29 | # 30 | # 2021-05-21: 31 | # * Fixed "object `x` not found" error in `imap()` (@mgirlich) 32 | # 33 | # 2020-04-14: 34 | # * Removed `pluck*()` functions 35 | # * Removed `*_cpl()` functions 36 | # * Used `as_function()` to allow use of `~` 37 | # * Used `.` prefix for helpers 38 | # 39 | # nocov start 40 | 41 | map <- function(.x, .f, ...) { 42 | .f <- as_function(.f, env = global_env()) 43 | lapply(.x, .f, ...) 44 | } 45 | walk <- function(.x, .f, ...) { 46 | map(.x, .f, ...) 47 | invisible(.x) 48 | } 49 | 50 | map_lgl <- function(.x, .f, ...) { 51 | .rlang_purrr_map_mold(.x, .f, logical(1), ...) 52 | } 53 | map_int <- function(.x, .f, ...) { 54 | .rlang_purrr_map_mold(.x, .f, integer(1), ...) 55 | } 56 | map_dbl <- function(.x, .f, ...) { 57 | .rlang_purrr_map_mold(.x, .f, double(1), ...) 58 | } 59 | map_chr <- function(.x, .f, ...) { 60 | .rlang_purrr_map_mold(.x, .f, character(1), ...) 61 | } 62 | .rlang_purrr_map_mold <- function(.x, .f, .mold, ...) { 63 | .f <- as_function(.f, env = global_env()) 64 | out <- vapply(.x, .f, .mold, ..., USE.NAMES = FALSE) 65 | names(out) <- names(.x) 66 | out 67 | } 68 | 69 | map2 <- function(.x, .y, .f, ...) { 70 | .f <- as_function(.f, env = global_env()) 71 | out <- mapply(.f, .x, .y, MoreArgs = list(...), SIMPLIFY = FALSE) 72 | if (length(out) == length(.x)) { 73 | set_names(out, names(.x)) 74 | } else { 75 | set_names(out, NULL) 76 | } 77 | } 78 | map2_lgl <- function(.x, .y, .f, ...) { 79 | as.vector(map2(.x, .y, .f, ...), "logical") 80 | } 81 | map2_int <- function(.x, .y, .f, ...) { 82 | as.vector(map2(.x, .y, .f, ...), "integer") 83 | } 84 | map2_dbl <- function(.x, .y, .f, ...) { 85 | as.vector(map2(.x, .y, .f, ...), "double") 86 | } 87 | map2_chr <- function(.x, .y, .f, ...) { 88 | as.vector(map2(.x, .y, .f, ...), "character") 89 | } 90 | imap <- function(.x, .f, ...) { 91 | map2(.x, names(.x) %||% seq_along(.x), .f, ...) 92 | } 93 | 94 | pmap <- function(.l, .f, ...) { 95 | .f <- as.function(.f) 96 | args <- .rlang_purrr_args_recycle(.l) 97 | do.call("mapply", c( 98 | FUN = list(quote(.f)), 99 | args, MoreArgs = quote(list(...)), 100 | SIMPLIFY = FALSE, USE.NAMES = FALSE 101 | )) 102 | } 103 | .rlang_purrr_args_recycle <- function(args) { 104 | lengths <- map_int(args, length) 105 | n <- max(lengths) 106 | 107 | stopifnot(all(lengths == 1L | lengths == n)) 108 | to_recycle <- lengths == 1L 109 | args[to_recycle] <- map(args[to_recycle], function(x) rep.int(x, n)) 110 | 111 | args 112 | } 113 | 114 | keep <- function(.x, .f, ...) { 115 | .x[.rlang_purrr_probe(.x, .f, ...)] 116 | } 117 | discard <- function(.x, .p, ...) { 118 | sel <- .rlang_purrr_probe(.x, .p, ...) 119 | .x[is.na(sel) | !sel] 120 | } 121 | map_if <- function(.x, .p, .f, ...) { 122 | matches <- .rlang_purrr_probe(.x, .p) 123 | .x[matches] <- map(.x[matches], .f, ...) 124 | .x 125 | } 126 | .rlang_purrr_probe <- function(.x, .p, ...) { 127 | if (is_logical(.p)) { 128 | stopifnot(length(.p) == length(.x)) 129 | .p 130 | } else { 131 | .p <- as_function(.p, env = global_env()) 132 | map_lgl(.x, .p, ...) 133 | } 134 | } 135 | 136 | compact <- function(.x) { 137 | Filter(length, .x) 138 | } 139 | 140 | transpose <- function(.l) { 141 | if (!length(.l)) { 142 | return(.l) 143 | } 144 | 145 | inner_names <- names(.l[[1]]) 146 | 147 | if (is.null(inner_names)) { 148 | fields <- seq_along(.l[[1]]) 149 | } else { 150 | fields <- set_names(inner_names) 151 | .l <- map(.l, function(x) { 152 | if (is.null(names(x))) { 153 | set_names(x, inner_names) 154 | } else { 155 | x 156 | } 157 | }) 158 | } 159 | 160 | # This way missing fields are subsetted as `NULL` instead of causing 161 | # an error 162 | .l <- map(.l, as.list) 163 | 164 | map(fields, function(i) { 165 | map(.l, .subset2, i) 166 | }) 167 | } 168 | 169 | every <- function(.x, .p, ...) { 170 | .p <- as_function(.p, env = global_env()) 171 | 172 | for (i in seq_along(.x)) { 173 | if (!rlang::is_true(.p(.x[[i]], ...))) return(FALSE) 174 | } 175 | TRUE 176 | } 177 | some <- function(.x, .p, ...) { 178 | .p <- as_function(.p, env = global_env()) 179 | 180 | for (i in seq_along(.x)) { 181 | if (rlang::is_true(.p(.x[[i]], ...))) return(TRUE) 182 | } 183 | FALSE 184 | } 185 | negate <- function(.p) { 186 | .p <- as_function(.p, env = global_env()) 187 | function(...) !.p(...) 188 | } 189 | 190 | reduce <- function(.x, .f, ..., .init) { 191 | f <- function(x, y) .f(x, y, ...) 192 | Reduce(f, .x, init = .init) 193 | } 194 | reduce_right <- function(.x, .f, ..., .init) { 195 | f <- function(x, y) .f(y, x, ...) 196 | Reduce(f, .x, init = .init, right = TRUE) 197 | } 198 | accumulate <- function(.x, .f, ..., .init) { 199 | f <- function(x, y) .f(x, y, ...) 200 | Reduce(f, .x, init = .init, accumulate = TRUE) 201 | } 202 | accumulate_right <- function(.x, .f, ..., .init) { 203 | f <- function(x, y) .f(y, x, ...) 204 | Reduce(f, .x, init = .init, right = TRUE, accumulate = TRUE) 205 | } 206 | 207 | detect <- function(.x, .f, ..., .right = FALSE, .p = is_true) { 208 | .p <- as_function(.p, env = global_env()) 209 | .f <- as_function(.f, env = global_env()) 210 | 211 | for (i in .rlang_purrr_index(.x, .right)) { 212 | if (.p(.f(.x[[i]], ...))) { 213 | return(.x[[i]]) 214 | } 215 | } 216 | NULL 217 | } 218 | detect_index <- function(.x, .f, ..., .right = FALSE, .p = is_true) { 219 | .p <- as_function(.p, env = global_env()) 220 | .f <- as_function(.f, env = global_env()) 221 | 222 | for (i in .rlang_purrr_index(.x, .right)) { 223 | if (.p(.f(.x[[i]], ...))) { 224 | return(i) 225 | } 226 | } 227 | 0L 228 | } 229 | .rlang_purrr_index <- function(x, right = FALSE) { 230 | idx <- seq_along(x) 231 | if (right) { 232 | idx <- rev(idx) 233 | } 234 | idx 235 | } 236 | 237 | list_c <- function(x) { 238 | inject(c(!!!x)) 239 | } 240 | 241 | # nocov end 242 | -------------------------------------------------------------------------------- /R/lifecycle-deprecated.R: -------------------------------------------------------------------------------- 1 | #' Select or rename variables 2 | #' 3 | #' @description 4 | #' `r lifecycle::badge("questioning")` 5 | #' 6 | #' Please use [eval_select()] and [eval_rename()] instead. See 7 | #' `vignette("tidyselect")` to get started. 8 | #' 9 | #' @param .vars A character vector of existing column names. 10 | #' @param ... Selection inputs. See the help for [selection 11 | #' helpers][language]. 12 | #' @param .include,.exclude Character vector of column names to always 13 | #' include/exclude. 14 | #' @param .strict If `TRUE`, will throw an error if you attempt to select or 15 | #' rename a variable that doesn't exist. 16 | #' @return A named character vector. Values are existing column names, 17 | #' names are new names. 18 | #' 19 | #' @seealso [vars_pull()] 20 | #' @export 21 | #' @keywords internal 22 | vars_select <- function(.vars, 23 | ..., 24 | .include = character(), 25 | .exclude = character(), 26 | .strict = TRUE) { 27 | dots <- enquos(...) 28 | if (!length(dots)) { 29 | signal("", "tidyselect_empty_dots") 30 | return(empty_sel(.vars, .include, .exclude)) 31 | } 32 | 33 | idx <- eval_select_impl( 34 | NULL, 35 | .vars, 36 | expr(c(!!!dots)), 37 | include = .include, 38 | exclude = .exclude, 39 | strict = .strict, 40 | name_spec = unique_name_spec, 41 | uniquely_named = TRUE, 42 | error_call = caller_env() 43 | ) 44 | 45 | sel <- set_names(.vars[idx], names(idx)) 46 | 47 | # Ensure all output are named, with `.vars` as default 48 | if (is_empty(sel)) { 49 | signal("", "tidyselect_empty") 50 | names(sel) <- chr() 51 | } else { 52 | unnamed <- names2(sel) == "" 53 | names(sel)[unnamed] <- sel[unnamed] 54 | } 55 | 56 | sel 57 | } 58 | 59 | empty_sel <- function(vars, include, exclude) { 60 | vars <- setdiff(include, exclude) 61 | set_names(vars, vars) 62 | } 63 | 64 | #' @export 65 | #' @rdname vars_select 66 | vars_rename <- function(.vars, ..., .strict = TRUE) { 67 | pos <- rename_impl( 68 | NULL, 69 | .vars, 70 | quo(c(...)), 71 | strict = .strict, 72 | error_call = caller_env() 73 | ) 74 | 75 | .vars <- set_names(.vars) 76 | names(.vars)[pos] <- names(pos) 77 | 78 | .vars 79 | } 80 | -------------------------------------------------------------------------------- /R/proxy.R: -------------------------------------------------------------------------------- 1 | #' tidyselect methods for custom types 2 | #' 3 | #' @description 4 | #' * `tidyselect_data_proxy()` returns a data frame. 5 | #' * `tidyselect_data_has_predicates()` returns `TRUE` or `FALSE` 6 | #' 7 | #' If your doesn't support predicate functions, return a 0-row data frame 8 | #' from `tidyselect_data_proxy()` and `FALSE` from 9 | #' `tidyselect_data_has_predicates()`. 10 | #' 11 | #' @param x A data-frame like object passed to [eval_select()], 12 | #' [eval_rename()], and friends. 13 | #' @export 14 | tidyselect_data_proxy <- function(x) { 15 | UseMethod("tidyselect_data_proxy") 16 | } 17 | #' @export 18 | tidyselect_data_proxy.default <- function(x) { 19 | x 20 | } 21 | 22 | 23 | #' @rdname tidyselect_data_proxy 24 | #' @export 25 | tidyselect_data_has_predicates <- function(x) { 26 | UseMethod("tidyselect_data_has_predicates") 27 | } 28 | #' @export 29 | tidyselect_data_has_predicates.default <- function(x) { 30 | TRUE 31 | } 32 | -------------------------------------------------------------------------------- /R/reexport-rlang.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | rlang::quo 3 | 4 | #' @export 5 | rlang::quos 6 | 7 | #' @export 8 | rlang::enquo 9 | 10 | #' @export 11 | rlang::quo_name 12 | -------------------------------------------------------------------------------- /R/sets.R: -------------------------------------------------------------------------------- 1 | 2 | # The `sel_` prefixed operations match on both values and names, with 3 | # unnamed elements matching named ones 4 | sel_union <- function(x, y) { 5 | if (any_valid_names(names(x)) || any_valid_names(names(y))) { 6 | sel_operation(x, y, vctrs::vec_set_union) 7 | } else { 8 | vctrs::vec_set_union(x, y) 9 | } 10 | } 11 | sel_intersect <- function(x, y) { 12 | if (any_valid_names(names(x)) || any_valid_names(names(y))) { 13 | sel_operation(x, y, vctrs::vec_set_intersect) 14 | } else { 15 | vctrs::vec_set_intersect(x, y) 16 | } 17 | } 18 | sel_unique <- function(x) { 19 | if (any_valid_names(names(x))) { 20 | x <- vctrs::new_data_frame(list(value = x, names = names2(x))) 21 | x <- propagate_names(x) 22 | out <- vctrs::vec_unique(x) 23 | set_names(out$value, out$names) 24 | } else { 25 | vctrs::vec_unique(x) 26 | } 27 | } 28 | 29 | # Set difference and set complement must validate their RHS eagerly, 30 | # otherwise OOB elements might be selected out and go unnoticed 31 | sel_diff <- function(x, y, vars = NULL, error_call = caller_env()) { 32 | if (!is_null(vars)) { 33 | y <- loc_validate(y, vars, call = error_call) 34 | } 35 | if (any_valid_names(names(x)) && any_valid_names(names(y))) { 36 | sel_operation(x, y, vctrs::vec_set_difference) 37 | } else { 38 | vctrs::vec_set_difference(x, y) 39 | } 40 | } 41 | sel_complement <- function(x, vars = NULL, error_call = caller_env()) { 42 | sel_diff(seq_along(vars), x, vars, error_call = error_call) 43 | } 44 | 45 | sel_operation <- function(x, y, sel_op) { 46 | x <- vctrs::new_data_frame(list(value = x, names = names2(x))) 47 | y <- vctrs::new_data_frame(list(value = y, names = names2(y))) 48 | 49 | x <- propagate_names(x, y) 50 | y <- propagate_names(y, x) 51 | 52 | out <- sel_op(x, y) 53 | set_names(out$value, out$names) 54 | } 55 | propagate_names <- function(x, from = NULL) { 56 | unnamed <- x$names == "" 57 | if (!any(unnamed)) { 58 | return(x) 59 | } 60 | 61 | # Match names inside `x` first, so we preserve order 62 | from <- vctrs::vec_c(x, from) 63 | 64 | # Prevent unnamed elements from matching 65 | vctrs::vec_slice(from$value, from$names == "") <- NA 66 | 67 | matches <- match( 68 | x$value[unnamed], 69 | from$value, 70 | nomatch = 0L 71 | ) 72 | x$names[unnamed][matches != 0L] <- from$names[matches] 73 | 74 | x 75 | } 76 | -------------------------------------------------------------------------------- /R/tidyselect-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | #' @import rlang 3 | #' @importFrom glue glue 4 | "_PACKAGE" 5 | 6 | ## usethis namespace: start 7 | ## usethis namespace: end 8 | NULL 9 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | select_loc <- function(x, 2 | sel, 3 | ..., 4 | include = NULL, 5 | exclude = NULL, 6 | strict = TRUE, 7 | name_spec = NULL, 8 | allow_rename = TRUE, 9 | allow_empty = TRUE, 10 | allow_predicates = TRUE, 11 | error_arg = NULL, 12 | error_call = current_env()) { 13 | check_dots_empty() 14 | 15 | eval_select( 16 | enquo(sel), 17 | x, 18 | include = include, 19 | exclude = exclude, 20 | strict = strict, 21 | name_spec = name_spec, 22 | allow_rename = allow_rename, 23 | allow_empty = allow_empty, 24 | allow_predicates = allow_predicates, 25 | error_arg = error_arg, 26 | error_call = error_call 27 | ) 28 | } 29 | 30 | rename_loc <- function(x, 31 | sel, 32 | ..., 33 | strict = TRUE, 34 | name_spec = NULL, 35 | error_call = current_env()) { 36 | check_dots_empty() 37 | rename_impl( 38 | x, 39 | names(x), 40 | enquo(sel), 41 | strict = strict, 42 | name_spec = name_spec, 43 | error_call = error_call 44 | ) 45 | } 46 | 47 | relocate_loc <- function(x, 48 | sel, 49 | ..., 50 | before = NULL, 51 | after = NULL, 52 | strict = TRUE, 53 | name_spec = NULL, 54 | allow_rename = TRUE, 55 | allow_empty = TRUE, 56 | before_arg = "before", 57 | after_arg = "after", 58 | error_arg = NULL, 59 | error_call = current_env()) { 60 | check_dots_empty() 61 | 62 | eval_relocate( 63 | expr = enquo(sel), 64 | data = x, 65 | before = enquo(before), 66 | after = enquo(after), 67 | strict = strict, 68 | name_spec = name_spec, 69 | allow_rename = allow_rename, 70 | allow_empty = allow_empty, 71 | before_arg = before_arg, 72 | after_arg = after_arg, 73 | error_arg = error_arg, 74 | error_call = error_call 75 | ) 76 | } 77 | 78 | any_valid_names <- function(nms) { 79 | if (is_null(nms)) { 80 | FALSE 81 | } else { 82 | !all(are_empty_name(nms)) 83 | } 84 | } 85 | 86 | are_empty_name <- function(nms) { 87 | if (!is_character(nms)) { 88 | abort("Expected a character vector") 89 | } 90 | 91 | nms == "" | is.na(nms) 92 | } 93 | 94 | # https://github.com/r-lib/vctrs/issues/571 95 | vec_is_coercible <- function(x, to, ..., x_arg = "x", to_arg = "to") { 96 | try_fetch( 97 | vctrs_error_incompatible_type = function(...) FALSE, 98 | { 99 | vctrs::vec_ptype2(x, to, ..., x_arg = x_arg, y_arg = to_arg) 100 | TRUE 101 | } 102 | ) 103 | } 104 | 105 | flat_map_int <- function(.x, .fn, ...) { 106 | out <- map(unname(.x), .fn, ...) 107 | vctrs::vec_c(!!!out, .ptype = int()) 108 | } 109 | 110 | loc_validate <- function(pos, vars, call = caller_env()) { 111 | check_missing(pos, call = call) 112 | check_negative(pos, call = call) 113 | 114 | pos <- vctrs::vec_as_subscript( 115 | pos, 116 | logical = "error", 117 | character = "error", 118 | call = call 119 | ) 120 | pos <- vctrs::vec_as_location( 121 | pos, 122 | n = length(vars), 123 | call = call 124 | ) 125 | 126 | named(sel_unique(pos)) 127 | } 128 | check_missing <- function(x, call) { 129 | if (anyNA(x)) { 130 | abort("Selections can't have missing values.", call = call) 131 | } 132 | } 133 | check_negative <- function(x, call) { 134 | if (any(x < 0L)) { 135 | abort("Selections can't have negative values.", call = call) 136 | } 137 | } 138 | 139 | quo_get_expr2 <- function(x, default) { 140 | if (is_quosure(x)) { 141 | quo_get_expr(x) 142 | } else { 143 | default 144 | } 145 | } 146 | quo_set_expr2 <- function(x, value, default) { 147 | if (is_quosure(x)) { 148 | quo_set_expr(x, value) 149 | } else { 150 | default 151 | } 152 | } 153 | 154 | # Always returns a fresh non-shared call 155 | call_expand_dots <- function(call, env) { 156 | if (!is_call(call)) { 157 | abort("`call` must be a call.") 158 | } 159 | 160 | call <- duplicate(call, shallow = TRUE) 161 | 162 | prev <- call 163 | node <- node_cdr(call) 164 | 165 | while (!is_null(node)) { 166 | if (is_symbol(node_car(node), "...")) { 167 | # Capture dots in a pairlist of quosures 168 | dots_mask <- env(env, enquos = enquos) 169 | dots <- eval_bare(quote(enquos(...)), dots_mask) 170 | dots <- as.pairlist(dots) 171 | 172 | # Splice the dots in the call 173 | if (!is_null(dots)) { 174 | node_poke_tail(dots, node_cdr(node)) 175 | } 176 | node_poke_cdr(prev, dots) 177 | 178 | break 179 | } 180 | 181 | prev <- node 182 | node <- node_cdr(node) 183 | } 184 | 185 | call 186 | } 187 | 188 | node_compact_missing <- function(node) { 189 | first <- new_node(NULL, node) 190 | prev <- first 191 | 192 | while (!is_null(node)) { 193 | car <- node_car(node) 194 | cdr <- node_cdr(node) 195 | 196 | if (is_missing(car) || (is_quosure(car) && quo_is_missing(car))) { 197 | node_poke_cdr(prev, cdr) 198 | } else { 199 | prev <- node 200 | } 201 | 202 | node <- cdr 203 | } 204 | 205 | node_cdr(first) 206 | } 207 | 208 | node_tail <- function(node) { 209 | rest <- node_cdr(node) 210 | 211 | while (!is_null(rest)) { 212 | node <- rest 213 | rest <- node_cdr(node) 214 | } 215 | 216 | node 217 | } 218 | 219 | node_poke_tail <- function(node, new) { 220 | node_poke_cdr(node_tail(node), new) 221 | } 222 | 223 | named <- function(x) { 224 | set_names(x, names2(x)) 225 | } 226 | 227 | mask_error_call <- function(data_mask) { 228 | data_mask$.__tidyselect__.$internal$error_call 229 | } 230 | 231 | paste_lines <- function(...) paste(c(...), collapse = "\n") 232 | 233 | check_dot_data <- function(expr, env, error_call) { 234 | if (is_quosure(expr)) { 235 | expr <- quo_get_expr(expr) 236 | } 237 | 238 | if (!is_call(expr, c("$", "[[")) || !identical(expr[[2]], quote(.data))) { 239 | return() 240 | } 241 | 242 | fn <- as_string(expr[[1]]) 243 | validate_dot_data(expr, error_call) 244 | 245 | what <- I("Use of .data in tidyselect expressions") 246 | if (fn == "$") { 247 | var <- as_string(expr[[3]]) 248 | str <- encodeString(var, quote = '"') 249 | 250 | lifecycle::deprecate_soft( 251 | "1.2.0", 252 | what, 253 | details = cli::format_inline("Please use {.code {str}} instead of `.data${var}`"), 254 | user_env = env 255 | ) 256 | } else if (fn == "[[") { 257 | # .data[[ is an injection operator so can't give specific advice 258 | lifecycle::deprecate_soft( 259 | "1.2.0", 260 | what, 261 | details = cli::format_inline("Please use {.code all_of(var)} (or {.code any_of(var)}) instead of {.code .data[[var]]}"), 262 | user_env = env 263 | ) 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /R/vars-pull.R: -------------------------------------------------------------------------------- 1 | #' Select variable 2 | #' 3 | #' This function powers [dplyr::pull()] and various functions of the 4 | #' tidyr package. It is similar to [vars_select()] but returns only 5 | #' one column name and has slightly different semantics: it allows 6 | #' negative numbers to select columns from the end. 7 | #' 8 | #' @inheritParams vars_select 9 | #' @inheritParams rlang::args_error_context 10 | #' @param var A variable specified as: 11 | #' * a literal variable name 12 | #' * a positive integer, giving the position counting from the left 13 | #' * a negative integer, giving the position counting from the right. 14 | #' 15 | #' The default returns the last column (on the assumption that's the 16 | #' column you've created most recently). 17 | #' 18 | #' This argument is taken by expression and supports 19 | #' [quasiquotation][rlang::quasiquotation] (you can unquote column 20 | #' names and column locations). 21 | #' @return The selected column name as an unnamed string. 22 | #' @seealso [dplyr::pull()], [vars_select()] 23 | #' @export 24 | #' @keywords internal 25 | #' @examples 26 | #' # It takes its argument by expression: 27 | #' vars_pull(letters, c) 28 | #' 29 | #' # Negative numbers select from the end: 30 | #' vars_pull(letters, -3) 31 | #' 32 | #' # You can unquote variables: 33 | #' var <- 10 34 | #' vars_pull(letters, !!var) 35 | vars_pull <- function(vars, var = -1, error_call = caller_env(), error_arg = caller_arg(var)) { 36 | expr <- enquo(var) 37 | 38 | if (quo_is_missing(expr)) { 39 | # No easy way to determine what var is in parent because it's likely 40 | # to be embraced; so don't try and use error_arg here 41 | cli::cli_abort( 42 | "{.arg var} is absent but must be supplied.", 43 | call = error_call 44 | ) 45 | } 46 | 47 | check_dot_data(expr, env = quo_get_env(expr), error_call = error_call) 48 | 49 | local_vars(vars) 50 | n <- length(vars) 51 | 52 | with_chained_errors( 53 | loc <- eval_tidy(expr, set_names(seq_along(vars), vars)), 54 | call = error_call, 55 | eval_expr = expr 56 | ) 57 | 58 | loc <- pull_as_location2(loc, n, vars, error_arg = error_arg, error_call = error_call) 59 | 60 | if (loc < 0L) { 61 | loc <- n + 1L + loc 62 | } 63 | vars[[loc]] 64 | } 65 | 66 | pull_as_location2 <- function(i, n, names, error_call = caller_env(), error_arg = "var") { 67 | with_subscript_errors(type = "pull", { 68 | i <- vctrs::vec_as_subscript2(i, 69 | logical = "error", 70 | arg = error_arg, 71 | call = error_call 72 | ) 73 | 74 | if (length(i) != 1) { 75 | cli::cli_abort( 76 | "{.arg {error_arg}} must select exactly one column.", 77 | call = error_call 78 | ) 79 | } 80 | 81 | if (is.numeric(i)) { 82 | vctrs::num_as_location2( 83 | i, 84 | n = n, 85 | negative = "ignore", 86 | arg = error_arg, 87 | call = error_call 88 | ) 89 | } else { 90 | vctrs::vec_as_location2( 91 | i, 92 | n = n, 93 | names = names, 94 | arg = error_arg, 95 | call = error_call, 96 | ) 97 | } 98 | }) 99 | } 100 | -------------------------------------------------------------------------------- /R/vars.R: -------------------------------------------------------------------------------- 1 | peeker <- function(what) { 2 | function(..., fn = NULL) { 3 | if (!missing(...)) { 4 | check_dots_empty() 5 | } 6 | 7 | x <- vars_env[[what]] 8 | 9 | if (is_null(x)) { 10 | if (is_null(fn)) { 11 | fn <- "Selection helpers" 12 | } else { 13 | fn <- glue::glue("`{fn}()`") 14 | } 15 | 16 | # Please keep in sync with faq.R. 17 | cli::cli_abort( 18 | c( 19 | "{fn} must be used within a *selecting* function.", 20 | i = "See {peek_vars_link()} for details." 21 | ), 22 | call = NULL 23 | ) 24 | } 25 | 26 | x 27 | } 28 | } 29 | 30 | peek_vars_link <- function() { 31 | if (is_interactive() && cli::ansi_has_hyperlink_support()) { 32 | topic <- "tidyselect::faq-selection-context" 33 | cli::style_hyperlink(paste0("?", topic), "ide:help", params = c(package = "tidyselect", topic = "faq-selection-context")) 34 | } else { 35 | "" 36 | } 37 | } 38 | 39 | #' Peek at variables in the selection context 40 | #' 41 | #' @description 42 | #' 43 | #' * `peek_vars()` returns the vector of names of the variables 44 | #' currently available for selection. 45 | #' 46 | #' * `peek_data()` returns the whole input vector (only available with 47 | #' [eval_select()]). 48 | #' 49 | #' Read the [Get 50 | #' started](https://tidyselect.r-lib.org/articles/tidyselect.html) for 51 | #' examples of how to create selection helpers with `peek_vars()`. 52 | #' 53 | #' The variable names in a selection context are registered 54 | #' automatically by [eval_select()] and [eval_rename()] for the 55 | #' duration of the evaluation. `peek_vars()` is the glue that connects 56 | #' [selection helpers][language] to the current selection 57 | #' context. 58 | #' 59 | #' @inheritParams rlang::args_dots_empty 60 | #' @param fn The name of the function to use in error messages when 61 | #' the helper is used in the wrong context. If not supplied, a 62 | #' generic error message is used instead. 63 | #' 64 | #' @export 65 | peek_vars <- peeker("selected") 66 | #' @rdname peek_vars 67 | #' @export 68 | peek_data <- peeker("data") 69 | 70 | #' Replace or get current variables 71 | #' 72 | #' @description 73 | #' 74 | #' Variables are made available to [select helpers][language] by 75 | #' registering them in a special placeholder. 76 | #' 77 | #' * `scoped_vars()` changes the current variables and sets up a 78 | #' function exit hook that automatically restores the previous 79 | #' variables once the current function returns. 80 | #' 81 | #' * `with_vars()` takes an expression to be evaluated in a variable 82 | #' context. 83 | #' 84 | #' * `poke_vars()` changes the contents of the placeholder with a new 85 | #' set of variables. It returns the previous variables invisibly and 86 | #' it is your responsibility to restore them after you are 87 | #' done. This is for expert use only. 88 | #' 89 | #' * `peek_vars()` returns the variables currently registered. 90 | #' 91 | #' * `has_vars()` returns `TRUE` if a variable context has been set, 92 | #' `FALSE` otherwise. 93 | #' 94 | #' @param vars A character vector of variable names. 95 | #' @return For `poke_vars()` and `scoped_vars()`, the old variables 96 | #' invisibly. For `peek_vars()`, the variables currently 97 | #' registered. 98 | #' 99 | #' @seealso peek_vars 100 | #' 101 | #' @export 102 | #' @keywords internal 103 | #' @examples 104 | #' poke_vars(letters) 105 | #' peek_vars() 106 | #' 107 | #' # Now that the variables are registered, the helpers can figure out 108 | #' # the locations of elements within the variable vector: 109 | #' all_of(c("d", "z")) 110 | #' 111 | #' # In a function be sure to restore the previous variables. An exit 112 | #' # hook is the best way to do it: 113 | #' fn <- function(vars) { 114 | #' old <- poke_vars(vars) 115 | #' on.exit(poke_vars(old)) 116 | #' 117 | #' all_of("d") 118 | #' } 119 | #' fn(letters) 120 | #' fn(letters[3:5]) 121 | #' 122 | #' # The previous variables are still registered after fn() was 123 | #' # called: 124 | #' peek_vars() 125 | #' 126 | #' 127 | #' # It is recommended to use the scoped variant as it restores the 128 | #' # state automatically when the function returns: 129 | #' fn <- function(vars) { 130 | #' scoped_vars(vars) 131 | #' starts_with("r") 132 | #' } 133 | #' fn(c("red", "blue", "rose")) 134 | #' 135 | #' # The with_vars() helper makes it easy to pass an expression that 136 | #' # should be evaluated in a variable context. Thanks to lazy 137 | #' # evaluation, you can just pass the expression argument from your 138 | #' # wrapper to with_vars(): 139 | #' fn <- function(expr) { 140 | #' vars <- c("red", "blue", "rose") 141 | #' with_vars(vars, expr) 142 | #' } 143 | #' fn(starts_with("r")) 144 | poke_vars <- function(vars) { 145 | if (!is_null(vars)) { 146 | vars <- vars_validate(vars) 147 | } 148 | 149 | old <- vars_env$selected 150 | vars_env$selected <- vars 151 | 152 | invisible(old) 153 | } 154 | poke_data <- function(data) { 155 | old <- vars_env$data 156 | vars_env$data <- data 157 | invisible(old) 158 | } 159 | 160 | #' @rdname poke_vars 161 | #' @param frame The frame environment where the exit hook for 162 | #' restoring the old variables should be registered. 163 | #' @export 164 | scoped_vars <- function(vars, frame = caller_env()) { 165 | old <- poke_vars(vars) 166 | withr::defer(poke_vars(old), envir = frame) 167 | invisible(old) 168 | } 169 | local_vars <- scoped_vars 170 | 171 | local_data <- function(data, frame = caller_env()) { 172 | old <- poke_data(data) 173 | withr::defer(poke_data(old), envir = frame) 174 | invisible(old) 175 | } 176 | 177 | #' @rdname poke_vars 178 | #' @param expr An expression to be evaluated within the variable 179 | #' context. 180 | #' @export 181 | with_vars <- function(vars, expr) { 182 | local_vars(vars) 183 | expr 184 | } 185 | 186 | #' @rdname poke_vars 187 | has_vars <- function() { 188 | !is_null(vars_env$selected) 189 | } 190 | 191 | vars_validate <- function(vars) { 192 | if (!is_character(vars)) { 193 | abort("`vars` must be a character vector") 194 | } 195 | 196 | # Named `vars` makes it harder to implement select helpers 197 | unname(vars) 198 | } 199 | 200 | vars_env <- new_environment() 201 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | #' List of selection helpers 2 | #' 3 | #' This list contains all selection helpers exported in tidyselect. It 4 | #' was useful when you wanted to embed the helpers in your API without 5 | #' having to track addition of new helpers in tidyselect. However the 6 | #' selection helpers are now always embedded in the DSL. 7 | #' 8 | #' @export 9 | #' @keywords internal 10 | vars_select_helpers <- list( 11 | starts_with = starts_with, 12 | ends_with = ends_with, 13 | contains = contains, 14 | matches = matches, 15 | num_range = num_range, 16 | one_of = one_of, 17 | everything = everything, 18 | last_col = last_col, 19 | all_of = all_of, 20 | any_of = any_of, 21 | where = where 22 | ) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tidyselect 2 | 3 | 4 | [![CRAN status](https://www.r-pkg.org/badges/version/tidyselect)](https://cran.r-project.org/package=tidyselect) 5 | [![R-CMD-check](https://github.com/r-lib/tidyselect/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/tidyselect/actions/workflows/R-CMD-check.yaml) 6 | [![Codecov test coverage](https://codecov.io/gh/r-lib/tidyselect/graph/badge.svg)](https://app.codecov.io/gh/r-lib/tidyselect) 7 | 8 | 9 | ## Overview 10 | 11 | The tidyselect package is the backend of functions like `dplyr::select()` 12 | or `dplyr::pull()` as well as several tidyr verbs. It allows you to 13 | create selecting verbs that are consistent with other tidyverse packages. 14 | 15 | * To learn about the selection syntax as a user of dplyr or tidyr, read 16 | the user-friendly `?language` reference. 17 | 18 | * To learn how to implement tidyselect in your own functions, read 19 | `vignette("tidyselect")`. 20 | 21 | * To learn exactly how the tidyselect syntax is interpreted, read the 22 | technical description in `vignette("syntax")`. 23 | 24 | ## Installation 25 | 26 | Generally, tidyselect will be installed automatically by the packages that need it. If you need to install it manually for some reason, you can get it with: 27 | 28 | ```r 29 | install.packages("tidyselect") 30 | ``` 31 | 32 | ## Code of Conduct 33 | 34 | Please note that the tidyselect project is released with a [Contributor Code of Conduct](https://github.com/r-lib/tidyselect/blob/main/.github/CODE_OF_CONDUCT.md). By contributing to this project, you agree to abide by its terms. 35 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://tidyselect.r-lib.org 2 | 3 | development: 4 | mode: auto 5 | 6 | template: 7 | package: tidytemplate 8 | bootstrap: 5 9 | 10 | includes: 11 | in_header: | 12 | 13 | 14 | reference: 15 | - title: Selecting variables 16 | - subtitle: Language and helpers 17 | contents: 18 | - select_helpers 19 | - starts_with("starts") 20 | - all_of 21 | - any_of 22 | - everything 23 | - last_col 24 | - where 25 | 26 | - subtitle: FAQ 27 | contents: 28 | - starts_with("faq") 29 | 30 | - title: Creating tidyselect API 31 | - subtitle: Evaluate expressions with tidyselect rules 32 | contents: 33 | - eval_select 34 | - eval_rename 35 | - eval_relocate 36 | - tidyselect_data_proxy 37 | 38 | - subtitle: Peek at current selection variables 39 | contents: 40 | - peek_vars 41 | 42 | news: 43 | releases: 44 | - text: "Version 1.2.0" 45 | href: "https://www.tidyverse.org/blog/2022/10/tidyselect-1-2-0/" 46 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## R CMD check results 2 | 3 | 0 errors | 0 warnings | 0 notes 4 | 5 | 6 | ## revdepcheck results 7 | 8 | The authors of ipumsr and pointblank were notified. The failures only concern superficial unit tests, no behaviour has been broken. 9 | 10 | We checked 485 reverse dependencies (452 from CRAN + 33 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. 11 | 12 | * We saw 2 new problems 13 | 14 | Issues with CRAN packages are summarised below. 15 | 16 | ### New problems 17 | (This reports the first line of each new failure) 18 | 19 | * ipumsr 20 | checking tests ... ERROR 21 | 22 | * pointblank 23 | checking tests ... ERROR 24 | -------------------------------------------------------------------------------- /man/all_of.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/helpers-vector.R 3 | \name{all_of} 4 | \alias{all_of} 5 | \alias{any_of} 6 | \title{Select variables from character vectors} 7 | \usage{ 8 | all_of(x) 9 | 10 | any_of(x, ..., vars = NULL) 11 | } 12 | \arguments{ 13 | \item{x}{A vector of character names or numeric locations.} 14 | 15 | \item{...}{These dots are for future extensions and must be empty.} 16 | 17 | \item{vars}{A character vector of variable names. If not supplied, 18 | the variables are taken from the current selection context (as 19 | established by functions like \code{select()} or \code{pivot_longer()}).} 20 | } 21 | \description{ 22 | These \link[=language]{selection helpers} select variables 23 | contained in a character vector. They are especially useful for 24 | programming with selecting functions. 25 | \itemize{ 26 | \item \code{\link[=all_of]{all_of()}} is for strict selection. If any of the variables in 27 | the character vector is missing, an error is thrown. 28 | \item \code{\link[=any_of]{any_of()}} doesn't check for missing variables. It is especially 29 | useful with negative selections, when you would like to make sure 30 | a variable is removed. 31 | } 32 | 33 | The order of selected columns is determined by the order in the 34 | vector. 35 | } 36 | \section{Examples}{ 37 | 38 | 39 | Selection helpers can be used in functions like \code{dplyr::select()} 40 | or \code{tidyr::pivot_longer()}. Let's first attach the tidyverse: 41 | 42 | \if{html}{\out{
}}\preformatted{library(tidyverse) 43 | 44 | # For better printing 45 | iris <- as_tibble(iris) 46 | }\if{html}{\out{
}} 47 | 48 | It is a common to have a names of variables in a vector. 49 | 50 | \if{html}{\out{
}}\preformatted{vars <- c("Sepal.Length", "Sepal.Width") 51 | 52 | iris[, vars] 53 | #> # A tibble: 150 x 2 54 | #> Sepal.Length Sepal.Width 55 | #> 56 | #> 1 5.1 3.5 57 | #> 2 4.9 3 58 | #> 3 4.7 3.2 59 | #> 4 4.6 3.1 60 | #> # i 146 more rows 61 | }\if{html}{\out{
}} 62 | 63 | To refer to these variables in selecting function, use \code{all_of()}: 64 | 65 | \if{html}{\out{
}}\preformatted{iris \%>\% select(all_of(vars)) 66 | #> # A tibble: 150 x 2 67 | #> Sepal.Length Sepal.Width 68 | #> 69 | #> 1 5.1 3.5 70 | #> 2 4.9 3 71 | #> 3 4.7 3.2 72 | #> 4 4.6 3.1 73 | #> # i 146 more rows 74 | 75 | iris \%>\% pivot_longer(all_of(vars)) 76 | #> # A tibble: 300 x 5 77 | #> Petal.Length Petal.Width Species name value 78 | #> 79 | #> 1 1.4 0.2 setosa Sepal.Length 5.1 80 | #> 2 1.4 0.2 setosa Sepal.Width 3.5 81 | #> 3 1.4 0.2 setosa Sepal.Length 4.9 82 | #> 4 1.4 0.2 setosa Sepal.Width 3 83 | #> # i 296 more rows 84 | }\if{html}{\out{
}} 85 | 86 | If any of these variables are missing from the data frame, that's an error: 87 | 88 | \if{html}{\out{
}}\preformatted{starwars \%>\% select(all_of(vars)) 89 | #> Error: 90 | #> i In argument: `all_of(vars)`. 91 | #> Caused by error in `all_of()`: 92 | #> ! Can't subset elements that don't exist. 93 | #> x Elements `Sepal.Length` and `Sepal.Width` don't exist. 94 | }\if{html}{\out{
}} 95 | 96 | Use \code{any_of()} to allow missing variables: 97 | 98 | \if{html}{\out{
}}\preformatted{starwars \%>\% select(any_of(vars)) 99 | #> # A tibble: 87 x 0 100 | }\if{html}{\out{
}} 101 | 102 | \code{any_of()} is especially useful to remove variables from a data 103 | frame because calling it again does not cause an error: 104 | 105 | \if{html}{\out{
}}\preformatted{iris \%>\% select(-any_of(vars)) 106 | #> # A tibble: 150 x 3 107 | #> Petal.Length Petal.Width Species 108 | #> 109 | #> 1 1.4 0.2 setosa 110 | #> 2 1.4 0.2 setosa 111 | #> 3 1.3 0.2 setosa 112 | #> 4 1.5 0.2 setosa 113 | #> # i 146 more rows 114 | 115 | iris \%>\% select(-any_of(vars)) \%>\% select(-any_of(vars)) 116 | #> # A tibble: 150 x 3 117 | #> Petal.Length Petal.Width Species 118 | #> 119 | #> 1 1.4 0.2 setosa 120 | #> 2 1.4 0.2 setosa 121 | #> 3 1.3 0.2 setosa 122 | #> 4 1.5 0.2 setosa 123 | #> # i 146 more rows 124 | }\if{html}{\out{
}} 125 | 126 | Supply named vectors to \code{all_of()} and \code{any_of()} to select and rename 127 | columns at the same time: 128 | 129 | \if{html}{\out{
}}\preformatted{colors <- c(color_of_hair = "hair_color", color_of_eyes = "eye_color") 130 | starwars \%>\% select(all_of(colors)) 131 | #> # A tibble: 87 x 2 132 | #> color_of_hair color_of_eyes 133 | #> 134 | #> 1 blond blue 135 | #> 2 yellow 136 | #> 3 red 137 | #> 4 none yellow 138 | #> # i 83 more rows 139 | }\if{html}{\out{
}} 140 | } 141 | 142 | \seealso{ 143 | The \link[=language]{selection language} page, which includes links to other selection helpers. 144 | } 145 | -------------------------------------------------------------------------------- /man/args_tidy_select.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/doc-tidy-selection.R 3 | \name{args_tidy_select} 4 | \alias{args_tidy_select} 5 | \title{Argument type: tidy-select} 6 | \description{ 7 | This page describes the \verb{} argument modifier which indicates 8 | the argument supports \strong{tidy selections}. Tidy selection provides a concise 9 | dialect of R for selecting variables based on their names or properties. 10 | 11 | Tidy selection is a variant of tidy evaluation. This means that inside 12 | functions, tidy-select arguments require special attention, as described in 13 | the \emph{Indirection} section below. If you've never heard of tidy evaluation 14 | before, start with \code{vignette("programming")}. 15 | } 16 | \section{Overview of selection features}{ 17 | Tidyverse selections implement a dialect of R where operators make 18 | it easy to select variables: 19 | \itemize{ 20 | \item \code{:} for selecting a range of consecutive variables. 21 | \item \code{!} for taking the complement of a set of variables. 22 | \item \code{&} and \code{|} for selecting the intersection or the union of two 23 | sets of variables. 24 | \item \code{c()} for combining selections. 25 | } 26 | 27 | In addition, you can use \strong{selection helpers}. Some helpers select specific 28 | columns: 29 | \itemize{ 30 | \item \code{\link[=everything]{everything()}}: Matches all variables. 31 | \item \code{\link[=last_col]{last_col()}}: Select last variable, possibly with an offset. 32 | } 33 | 34 | Other helpers select variables by matching patterns in their names: 35 | \itemize{ 36 | \item \code{\link[=starts_with]{starts_with()}}: Starts with a prefix. 37 | \item \code{\link[=ends_with]{ends_with()}}: Ends with a suffix. 38 | \item \code{\link[=contains]{contains()}}: Contains a literal string. 39 | \item \code{\link[=matches]{matches()}}: Matches a regular expression. 40 | \item \code{\link[=num_range]{num_range()}}: Matches a numerical range like x01, x02, x03. 41 | } 42 | 43 | Or from variables stored in a character vector: 44 | \itemize{ 45 | \item \code{\link[=all_of]{all_of()}}: Matches variable names in a character vector. All 46 | names must be present, otherwise an out-of-bounds error is 47 | thrown. 48 | \item \code{\link[=any_of]{any_of()}}: Same as \code{all_of()}, except that no error is thrown 49 | for names that don't exist. 50 | } 51 | 52 | Or using a predicate function: 53 | \itemize{ 54 | \item \code{\link[=where]{where()}}: Applies a function to all variables and selects those 55 | for which the function returns \code{TRUE}. 56 | } 57 | } 58 | 59 | \section{Indirection}{ 60 | There are two main cases: 61 | \itemize{ 62 | \item If you have a character vector of column names, use \code{all_of()} 63 | or \code{any_of()}, depending on whether or not you want unknown variable 64 | names to cause an error, e.g. \code{select(df, all_of(vars))}, 65 | \code{select(df, !any_of(vars))}. 66 | \item If you want the user to be able to supply a tidyselect specification in 67 | a function argument, embrace the function argument, e.g. 68 | \code{select(df, {{ vars }})}. 69 | } 70 | } 71 | 72 | \keyword{internal} 73 | -------------------------------------------------------------------------------- /man/eval_relocate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/eval-relocate.R 3 | \name{eval_relocate} 4 | \alias{eval_relocate} 5 | \title{Evaluate an expression to relocate variables} 6 | \usage{ 7 | eval_relocate( 8 | expr, 9 | data, 10 | ..., 11 | before = NULL, 12 | after = NULL, 13 | strict = TRUE, 14 | name_spec = NULL, 15 | allow_rename = TRUE, 16 | allow_empty = TRUE, 17 | allow_predicates = TRUE, 18 | before_arg = "before", 19 | after_arg = "after", 20 | env = caller_env(), 21 | error_arg = NULL, 22 | error_call = caller_env() 23 | ) 24 | } 25 | \arguments{ 26 | \item{expr}{Defused R code describing a selection according to the 27 | tidyselect syntax.} 28 | 29 | \item{data}{A named list, data frame, or atomic vector. 30 | Technically, \code{data} can be any vector with \code{names()} and \code{"[["} 31 | implementations.} 32 | 33 | \item{...}{These dots are for future extensions and must be empty.} 34 | 35 | \item{before, after}{Defused R code describing a selection according to the 36 | tidyselect syntax. The selection represents the destination of the 37 | selection provided through \code{expr}. Supplying neither of these will move the 38 | selection to the left-hand side. Supplying both of these is an error.} 39 | 40 | \item{strict}{If \code{TRUE}, out-of-bounds errors are thrown if \code{expr} 41 | attempts to select or rename a variable that doesn't exist. If 42 | \code{FALSE}, failed selections or renamings are ignored.} 43 | 44 | \item{name_spec}{A name specification describing how to combine or 45 | propagate names. This is used only in case nested \code{c()} 46 | expressions like \code{c(foo = c(bar = starts_with("foo")))}. See the 47 | \code{name_spec} argument of \code{\link[vctrs:vec_c]{vctrs::vec_c()}} for a description of 48 | valid name specs.} 49 | 50 | \item{allow_rename}{If \code{TRUE} (the default), the renaming syntax 51 | \code{c(foo = bar)} is allowed. If \code{FALSE}, it causes an error. This 52 | is useful to implement purely selective behaviour.} 53 | 54 | \item{allow_empty}{If \code{TRUE} (the default), it is ok for \code{expr} to result 55 | in an empty selection. If \code{FALSE}, will error if \code{expr} yields an empty 56 | selection.} 57 | 58 | \item{allow_predicates}{If \code{TRUE} (the default), it is ok for \code{expr} to 59 | use predicates (i.e. in \code{where()}). If \code{FALSE}, will error if \code{expr} uses a 60 | predicate. Will automatically be set to \code{FALSE} if \code{data} does not 61 | support predicates (as determined by \code{\link[=tidyselect_data_has_predicates]{tidyselect_data_has_predicates()}}).} 62 | 63 | \item{before_arg, after_arg}{Argument names for \code{before} and \code{after}. These 64 | are used in error messages.} 65 | 66 | \item{env}{The environment in which to evaluate \code{expr}. Discarded 67 | if \code{expr} is a \link[rlang:enquo]{quosure}.} 68 | 69 | \item{error_arg}{Argument names for \code{expr}. These 70 | are used in error messages. (You can use \code{"..."} if \code{expr = c(...)}).} 71 | 72 | \item{error_call}{The execution environment of a currently 73 | running function, e.g. \code{caller_env()}. The function will be 74 | mentioned in error messages as the source of the error. See the 75 | \code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.} 76 | } 77 | \value{ 78 | A named vector of numeric locations with length equal to \code{length(data)}. 79 | Each position in \code{data} will be represented exactly once. 80 | 81 | The names are normally the same as in the input data, except when the user 82 | supplied named selections with \code{c()}. In the latter case, the names reflect 83 | the new names chosen by the user. 84 | } 85 | \description{ 86 | \code{eval_relocate()} is a variant of \code{\link[=eval_select]{eval_select()}} that moves a selection to 87 | a new location. Either \code{before} or \code{after} can be provided to specify where 88 | to move the selection to. This powers \code{dplyr::relocate()}. 89 | } 90 | \examples{ 91 | library(rlang) 92 | 93 | # Interpret defused code as a request to relocate 94 | x <- expr(c(mpg, disp)) 95 | after <- expr(wt) 96 | eval_relocate(x, mtcars, after = after) 97 | 98 | # Supplying neither `before` nor `after` will move the selection to the 99 | # left-hand side 100 | eval_relocate(x, mtcars) 101 | 102 | # Within a function, use `enquo()` to defuse a single argument. 103 | # Note that `before` and `after` must also be defused with `enquo()`. 104 | my_relocator <- function(x, expr, before = NULL, after = NULL) { 105 | eval_relocate(enquo(expr), x, before = enquo(before), after = enquo(after)) 106 | } 107 | 108 | my_relocator(mtcars, vs, before = hp) 109 | 110 | # Here is an example of using `eval_relocate()` to implement `relocate()`. 111 | # Note that the dots are passed on as a defused call to `c(...)`. 112 | relocate <- function(.x, ..., .before = NULL, .after = NULL) { 113 | pos <- eval_relocate( 114 | expr(c(...)), 115 | .x, 116 | before = enquo(.before), 117 | after = enquo(.after) 118 | ) 119 | set_names(.x[pos], names(pos)) 120 | } 121 | 122 | relocate(mtcars, vs, .before = hp) 123 | relocate(mtcars, starts_with("d"), .after = last_col()) 124 | } 125 | -------------------------------------------------------------------------------- /man/eval_select.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/eval-rename.R, R/eval-select.R 3 | \name{eval_rename} 4 | \alias{eval_rename} 5 | \alias{eval_select} 6 | \title{Evaluate an expression with tidyselect semantics} 7 | \usage{ 8 | eval_rename( 9 | expr, 10 | data, 11 | env = caller_env(), 12 | ..., 13 | strict = TRUE, 14 | name_spec = NULL, 15 | allow_predicates = TRUE, 16 | error_call = caller_env() 17 | ) 18 | 19 | eval_select( 20 | expr, 21 | data, 22 | env = caller_env(), 23 | ..., 24 | include = NULL, 25 | exclude = NULL, 26 | strict = TRUE, 27 | name_spec = NULL, 28 | allow_rename = TRUE, 29 | allow_empty = TRUE, 30 | allow_predicates = TRUE, 31 | error_arg = NULL, 32 | error_call = caller_env() 33 | ) 34 | } 35 | \arguments{ 36 | \item{expr}{Defused R code describing a selection according to the 37 | tidyselect syntax.} 38 | 39 | \item{data}{A named list, data frame, or atomic vector. 40 | Technically, \code{data} can be any vector with \code{names()} and \code{"[["} 41 | implementations.} 42 | 43 | \item{env}{The environment in which to evaluate \code{expr}. Discarded 44 | if \code{expr} is a \link[rlang:enquo]{quosure}.} 45 | 46 | \item{...}{These dots are for future extensions and must be empty.} 47 | 48 | \item{strict}{If \code{TRUE}, out-of-bounds errors are thrown if \code{expr} 49 | attempts to select or rename a variable that doesn't exist. If 50 | \code{FALSE}, failed selections or renamings are ignored.} 51 | 52 | \item{name_spec}{A name specification describing how to combine or 53 | propagate names. This is used only in case nested \code{c()} 54 | expressions like \code{c(foo = c(bar = starts_with("foo")))}. See the 55 | \code{name_spec} argument of \code{\link[vctrs:vec_c]{vctrs::vec_c()}} for a description of 56 | valid name specs.} 57 | 58 | \item{allow_predicates}{If \code{TRUE} (the default), it is ok for \code{expr} to 59 | use predicates (i.e. in \code{where()}). If \code{FALSE}, will error if \code{expr} uses a 60 | predicate. Will automatically be set to \code{FALSE} if \code{data} does not 61 | support predicates (as determined by \code{\link[=tidyselect_data_has_predicates]{tidyselect_data_has_predicates()}}).} 62 | 63 | \item{error_call}{The execution environment of a currently 64 | running function, e.g. \code{caller_env()}. The function will be 65 | mentioned in error messages as the source of the error. See the 66 | \code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.} 67 | 68 | \item{include, exclude}{Character vector of column names to always 69 | include or exclude from the selection.} 70 | 71 | \item{allow_rename}{If \code{TRUE} (the default), the renaming syntax 72 | \code{c(foo = bar)} is allowed. If \code{FALSE}, it causes an error. This 73 | is useful to implement purely selective behaviour.} 74 | 75 | \item{allow_empty}{If \code{TRUE} (the default), it is ok for \code{expr} to result 76 | in an empty selection. If \code{FALSE}, will error if \code{expr} yields an empty 77 | selection.} 78 | 79 | \item{error_arg}{Argument names for \code{expr}. These 80 | are used in error messages. (You can use \code{"..."} if \code{expr = c(...)}).} 81 | } 82 | \value{ 83 | A named vector of numeric locations, one for each of the 84 | selected elements. 85 | 86 | The names are normally the same as in the input data, except when 87 | the user supplied named selections with \code{c()}. In the latter 88 | case, the names reflect the new names chosen by the user. 89 | 90 | A given element may be selected multiple times under different 91 | names, in which case the vector might contain duplicate 92 | locations. 93 | } 94 | \description{ 95 | \code{eval_select()} and \code{eval_rename()} evaluate defused R code 96 | (i.e. quoted expressions) according to the special rules of the 97 | \href{https://tidyselect.r-lib.org/articles/syntax.html}{tidyselect syntax}. They 98 | power functions like \code{dplyr::select()}, \code{dplyr::rename()}, or 99 | \code{tidyr::pivot_longer()}. 100 | 101 | See the \href{https://tidyselect.r-lib.org/articles/tidyselect.html}{Get started} 102 | vignette to learn how to use \code{eval_select()} and \code{eval_rename()} in 103 | your packages. 104 | } 105 | \details{ 106 | The select and rename variants take the same types of inputs and 107 | have the same type of return value. However \code{eval_rename()} has a 108 | few extra constraints. It requires named inputs, and will fail if a 109 | data frame column is renamed to another existing column name. See 110 | the \href{https://tidyselect.r-lib.org/articles/syntax.html}{selecting versus renaming} 111 | section in the syntax vignette for a description of the 112 | differences. 113 | } 114 | \examples{ 115 | library(rlang) 116 | 117 | # Interpret defused code as selection: 118 | x <- expr(mpg:cyl) 119 | eval_select(x, mtcars) 120 | 121 | # Interpret defused code as a renaming selection. All inputs must 122 | # be named within `c()`: 123 | try(eval_rename(expr(mpg), mtcars)) 124 | eval_rename(expr(c(foo = mpg)), mtcars) 125 | 126 | 127 | # Within a function, use `enquo()` to defuse one argument: 128 | my_function <- function(x, expr) { 129 | eval_select(enquo(expr), x) 130 | } 131 | 132 | # If your function takes dots, evaluate a defused call to `c(...)` 133 | # with `expr(c(...))`: 134 | my_function <- function(.x, ...) { 135 | eval_select(expr(c(...)), .x) 136 | } 137 | 138 | # If your function takes dots and a named argument, use `{{ }}` 139 | # inside the defused expression to tunnel it inside the tidyselect DSL: 140 | my_function <- function(.x, .expr, ...) { 141 | eval_select(expr(c({{ .expr }}, ...)), .x) 142 | } 143 | 144 | # Note that the trick above works because `expr({{ arg }})` is the 145 | # same as `enquo(arg)`. 146 | 147 | # Supply `error_arg` to improve the error message in case of 148 | # unexpected empty selection: 149 | select_not_empty <- function(x, cols) { 150 | eval_select(expr = enquo(cols), data = x, allow_empty = FALSE, error_arg = "cols") 151 | } 152 | try(select_not_empty(mtcars, cols = starts_with("vs2"))) 153 | 154 | # The evaluators return a named vector of locations. Here are 155 | # examples of using these location vectors to implement `select()` 156 | # and `rename()`: 157 | select <- function(.x, ...) { 158 | pos <- eval_select(expr(c(...)), .x) 159 | set_names(.x[pos], names(pos)) 160 | } 161 | rename <- function(.x, ...) { 162 | pos <- eval_rename(expr(c(...)), .x) 163 | names(.x)[pos] <- names(pos) 164 | .x 165 | } 166 | 167 | select(mtcars, mpg:cyl) 168 | rename(mtcars, foo = mpg) 169 | } 170 | \seealso{ 171 | \url{https://tidyselect.r-lib.org/articles/syntax.html} or 172 | \code{vignette("syntax", package = "tidyselect")} for a technical 173 | description of the rules of evaluation. 174 | } 175 | -------------------------------------------------------------------------------- /man/everything.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/helpers-misc.R 3 | \name{everything} 4 | \alias{everything} 5 | \alias{last_col} 6 | \title{Select all variables or the last variable} 7 | \usage{ 8 | everything(vars = NULL) 9 | 10 | last_col(offset = 0L, vars = NULL) 11 | } 12 | \arguments{ 13 | \item{vars}{A character vector of variable names. If not supplied, 14 | the variables are taken from the current selection context (as 15 | established by functions like \code{select()} or \code{pivot_longer()}).} 16 | 17 | \item{offset}{Set it to \code{n} to select the nth var from the end.} 18 | } 19 | \description{ 20 | These functions are \link[=language]{selection helpers}. 21 | \itemize{ 22 | \item \code{\link[=everything]{everything()}} selects all variable. It is also useful in 23 | combination with other tidyselect operators. 24 | \item \code{\link[=last_col]{last_col()}} selects the last variable. 25 | } 26 | } 27 | \section{Examples}{ 28 | 29 | 30 | Selection helpers can be used in functions like \code{dplyr::select()} 31 | or \code{tidyr::pivot_longer()}. Let's first attach the tidyverse: 32 | 33 | \if{html}{\out{
}}\preformatted{library(tidyverse) 34 | 35 | # For better printing 36 | iris <- as_tibble(iris) 37 | mtcars <- as_tibble(mtcars) 38 | }\if{html}{\out{
}} 39 | 40 | Use \code{everything()} to select all variables: 41 | 42 | \if{html}{\out{
}}\preformatted{iris \%>\% select(everything()) 43 | #> # A tibble: 150 x 5 44 | #> Sepal.Length Sepal.Width Petal.Length Petal.Width Species 45 | #> 46 | #> 1 5.1 3.5 1.4 0.2 setosa 47 | #> 2 4.9 3 1.4 0.2 setosa 48 | #> 3 4.7 3.2 1.3 0.2 setosa 49 | #> 4 4.6 3.1 1.5 0.2 setosa 50 | #> # i 146 more rows 51 | 52 | mtcars \%>\% pivot_longer(everything()) 53 | #> # A tibble: 352 x 2 54 | #> name value 55 | #> 56 | #> 1 mpg 21 57 | #> 2 cyl 6 58 | #> 3 disp 160 59 | #> 4 hp 110 60 | #> # i 348 more rows 61 | }\if{html}{\out{
}} 62 | 63 | Use \code{last_col()} to select the last variable: 64 | 65 | \if{html}{\out{
}}\preformatted{iris \%>\% select(last_col()) 66 | #> # A tibble: 150 x 1 67 | #> Species 68 | #> 69 | #> 1 setosa 70 | #> 2 setosa 71 | #> 3 setosa 72 | #> 4 setosa 73 | #> # i 146 more rows 74 | 75 | mtcars \%>\% pivot_longer(last_col()) 76 | #> # A tibble: 32 x 12 77 | #> mpg cyl disp hp drat wt qsec vs am gear name value 78 | #> 79 | #> 1 21 6 160 110 3.9 2.62 16.5 0 1 4 carb 4 80 | #> 2 21 6 160 110 3.9 2.88 17.0 0 1 4 carb 4 81 | #> 3 22.8 4 108 93 3.85 2.32 18.6 1 1 4 carb 1 82 | #> 4 21.4 6 258 110 3.08 3.22 19.4 1 0 3 carb 1 83 | #> # i 28 more rows 84 | }\if{html}{\out{
}} 85 | 86 | Supply an offset \code{n} to select a variable located \code{n} positions 87 | from the end: 88 | 89 | \if{html}{\out{
}}\preformatted{mtcars \%>\% select(1:last_col(5)) 90 | #> # A tibble: 32 x 6 91 | #> mpg cyl disp hp drat wt 92 | #> 93 | #> 1 21 6 160 110 3.9 2.62 94 | #> 2 21 6 160 110 3.9 2.88 95 | #> 3 22.8 4 108 93 3.85 2.32 96 | #> 4 21.4 6 258 110 3.08 3.22 97 | #> # i 28 more rows 98 | }\if{html}{\out{
}} 99 | } 100 | 101 | \seealso{ 102 | The \link[=language]{selection language} page, which includes links to other selection helpers. 103 | } 104 | -------------------------------------------------------------------------------- /man/faq-external-vector.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/faq.R 3 | \name{faq-external-vector} 4 | \alias{faq-external-vector} 5 | \title{FAQ - Note: Using an external vector in selections is ambiguous} 6 | \description{ 7 | \subsection{Ambiguity between columns and external variables}{ 8 | 9 | With selecting functions like \code{dplyr::select()} or 10 | \code{tidyr::pivot_longer()}, you can refer to variables by name: 11 | 12 | \if{html}{\out{
}}\preformatted{mtcars \%>\% select(cyl, am, vs) 13 | #> # A tibble: 32 x 3 14 | #> cyl am vs 15 | #> 16 | #> 1 6 1 0 17 | #> 2 6 1 0 18 | #> 3 4 1 1 19 | #> 4 6 0 1 20 | #> # i 28 more rows 21 | 22 | mtcars \%>\% select(mpg:disp) 23 | #> # A tibble: 32 x 3 24 | #> mpg cyl disp 25 | #> 26 | #> 1 21 6 160 27 | #> 2 21 6 160 28 | #> 3 22.8 4 108 29 | #> 4 21.4 6 258 30 | #> # i 28 more rows 31 | }\if{html}{\out{
}} 32 | 33 | For historical reasons, it is also possible to refer an external vector 34 | of variable names. You get the correct result, but with a warning 35 | informing you that selecting with an external variable is ambiguous 36 | because it is not clear whether you want a data frame column or an 37 | external object. 38 | 39 | \if{html}{\out{
}}\preformatted{vars <- c("cyl", "am", "vs") 40 | result <- mtcars \%>\% select(vars) 41 | #> Warning: Using an external vector in selections was deprecated in tidyselect 1.1.0. 42 | #> i Please use `all_of()` or `any_of()` instead. 43 | #> i See . 44 | #> This warning is displayed once every 8 hours. 45 | #> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was 46 | #> generated. 47 | }\if{html}{\out{
}} 48 | 49 | We have decided to deprecate this particular approach to using external 50 | vectors because they introduce ambiguity. Imagine that the data frame 51 | contains a column with the same name as your external variable. 52 | 53 | \if{html}{\out{
}}\preformatted{some_df <- mtcars[1:4, ] 54 | some_df$vars <- 1:nrow(some_df) 55 | }\if{html}{\out{
}} 56 | 57 | These are very different objects but it isn’t a problem if the context 58 | forces you to be specific about where to find \code{vars}: 59 | 60 | \if{html}{\out{
}}\preformatted{vars 61 | #> [1] "cyl" "am" "vs" 62 | 63 | some_df$vars 64 | #> [1] 1 2 3 4 65 | }\if{html}{\out{
}} 66 | 67 | In a selection context however, the column wins: 68 | 69 | \if{html}{\out{
}}\preformatted{some_df \%>\% select(vars) 70 | #> # A tibble: 4 x 1 71 | #> vars 72 | #> 73 | #> 1 1 74 | #> 2 2 75 | #> 3 3 76 | #> 4 4 77 | }\if{html}{\out{
}} 78 | } 79 | 80 | \subsection{Fixing the ambiguity}{ 81 | 82 | To make your selection code more robust and silence the message, use 83 | \code{all_of()} to force the external vector: 84 | 85 | \if{html}{\out{
}}\preformatted{some_df \%>\% select(all_of(vars)) 86 | #> # A tibble: 4 x 3 87 | #> cyl am vs 88 | #> 89 | #> 1 6 1 0 90 | #> 2 6 1 0 91 | #> 3 4 1 1 92 | #> 4 6 0 1 93 | }\if{html}{\out{
}} 94 | 95 | For more information or if you have comments about this, please see the 96 | \href{https://github.com/r-lib/tidyselect/issues/76}{Github issue} tracking 97 | the deprecation process. 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /man/faq-selection-context.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/faq.R 3 | \name{faq-selection-context} 4 | \alias{faq-selection-context} 5 | \title{FAQ - Error: Must be used within a \emph{selecting} function} 6 | \description{ 7 | Functions like \code{starts_with()}, \code{contains()} or \code{matches()} are 8 | \strong{selection helpers} that only work in a selection context, e.g. 9 | \code{dplyr::select()} or the \code{cols} argument of \code{tidyr::pivot_longer()}. 10 | 11 | Using a selection helper anywhere else results in an error: 12 | 13 | \if{html}{\out{
}}\preformatted{starts_with("foo") 14 | #> Error: 15 | #> ! `starts_with()` must be used within a *selecting* function. 16 | #> i See for 17 | #> details. 18 | 19 | mtcars[contains("foo")] 20 | #> Error: 21 | #> ! `contains()` must be used within a *selecting* function. 22 | #> i See for 23 | #> details. 24 | 25 | subset(mtcars, select = matches("foo")) 26 | #> Error: 27 | #> ! `matches()` must be used within a *selecting* function. 28 | #> i See for 29 | #> details. 30 | }\if{html}{\out{
}} 31 | 32 | If you see this error, you may have used a selection helper in the wrong 33 | place, possibly as the result of a typo (e.g. misplaced comma or wrong 34 | argument name). Alternatively, you may be deliberately trying to reduce 35 | duplication in your code by extracting out a selection into a variable: 36 | 37 | \if{html}{\out{
}}\preformatted{my_vars <- c(name, species, ends_with("color")) 38 | #> Error: object 'name' not found 39 | }\if{html}{\out{
}} 40 | 41 | To make this work you’ll need to do two things: 42 | \itemize{ 43 | \item Wrap the whole thing in a function 44 | \item Use \code{any_of()} or \code{all_of()} instead of bare variable names 45 | } 46 | 47 | \if{html}{\out{
}}\preformatted{my_vars <- function() \{ 48 | c(any_of(c("name", "species")), ends_with("color")) 49 | \} 50 | dplyr::select(starwars, my_vars()) 51 | #> # A tibble: 87 x 5 52 | #> name species hair_color skin_color eye_color 53 | #> 54 | #> 1 Luke Skywalker Human blond fair blue 55 | #> 2 C-3PO Droid gold yellow 56 | #> 3 R2-D2 Droid white, blue red 57 | #> 4 Darth Vader Human none white yellow 58 | #> # i 83 more rows 59 | }\if{html}{\out{
}} 60 | } 61 | -------------------------------------------------------------------------------- /man/faq/external-vector.Rmd: -------------------------------------------------------------------------------- 1 | 2 | ```{r} 3 | #| child: "setup.Rmd" 4 | #| include: FALSE 5 | ``` 6 | 7 | ## Ambiguity between columns and external variables 8 | 9 | With selecting functions like `dplyr::select()` or `tidyr::pivot_longer()`, 10 | you can refer to variables by name: 11 | 12 | ```{r} 13 | mtcars %>% select(cyl, am, vs) 14 | 15 | mtcars %>% select(mpg:disp) 16 | ``` 17 | 18 | For historical reasons, it is also possible to refer an external 19 | vector of variable names. You get the correct result, but with a 20 | warning informing you that selecting with an external variable is 21 | ambiguous because it is not clear whether you want a data frame column 22 | or an external object. 23 | 24 | ```{r} 25 | vars <- c("cyl", "am", "vs") 26 | result <- mtcars %>% select(vars) 27 | ``` 28 | 29 | We have decided to deprecate this particular approach to using 30 | external vectors because they introduce ambiguity. Imagine that the 31 | data frame contains a column with the same name as your external 32 | variable. 33 | 34 | ```{r} 35 | some_df <- mtcars[1:4, ] 36 | some_df$vars <- 1:nrow(some_df) 37 | ``` 38 | 39 | These are very different objects but it isn't a problem if the context 40 | forces you to be specific about where to find `vars`: 41 | 42 | ```{r} 43 | vars 44 | 45 | some_df$vars 46 | ``` 47 | 48 | In a selection context however, the column wins: 49 | 50 | ```{r} 51 | some_df %>% select(vars) 52 | ``` 53 | 54 | 55 | ## Fixing the ambiguity 56 | 57 | To make your selection code more robust and silence the message, use 58 | `all_of()` to force the external vector: 59 | 60 | ```{r} 61 | some_df %>% select(all_of(vars)) 62 | ``` 63 | 64 | For more information or if you have comments about this, please see 65 | the [Github issue](https://github.com/r-lib/tidyselect/issues/76) 66 | tracking the deprecation process. 67 | -------------------------------------------------------------------------------- /man/faq/selection-context.Rmd: -------------------------------------------------------------------------------- 1 | 2 | ```{r} 3 | #| child: "setup.Rmd" 4 | #| include: FALSE 5 | ``` 6 | 7 | Functions like `starts_with()`, `contains()` or `matches()` are 8 | __selection helpers__ that only work in a selection context, e.g. 9 | `dplyr::select()` or the `cols` argument of `tidyr::pivot_longer()`. 10 | 11 | Using a selection helper anywhere else results in an error: 12 | 13 | ```{r, error = TRUE} 14 | starts_with("foo") 15 | 16 | mtcars[contains("foo")] 17 | 18 | subset(mtcars, select = matches("foo")) 19 | ``` 20 | 21 | If you see this error, you may have used a selection helper in the 22 | wrong place, possibly as the result of a typo (e.g. misplaced comma or 23 | wrong argument name). Alternatively, you may be deliberately trying 24 | to reduce duplication in your code by extracting out a selection into 25 | a variable: 26 | 27 | ```{r, error = TRUE} 28 | my_vars <- c(name, species, ends_with("color")) 29 | ``` 30 | 31 | To make this work you'll need to do two things: 32 | 33 | * Wrap the whole thing in a function 34 | * Use `any_of()` or `all_of()` instead of bare variable names 35 | 36 | ```{r} 37 | my_vars <- function() { 38 | c(any_of(c("name", "species")), ends_with("color")) 39 | } 40 | dplyr::select(starwars, my_vars()) 41 | ``` 42 | -------------------------------------------------------------------------------- /man/faq/setup.Rmd: -------------------------------------------------------------------------------- 1 | 2 | ```{r} 3 | #| include: FALSE 4 | knitr::opts_chunk$set( 5 | collapse = TRUE, 6 | comment = "#>" 7 | ) 8 | 9 | library(tidyselect) 10 | library(magrittr) 11 | 12 | # For examples 13 | rename <- function(.x, ..., .strict = TRUE) { 14 | pos <- eval_rename(rlang::expr(c(...)), .x, strict = .strict) 15 | names(.x)[pos] <- names(pos) 16 | .x 17 | } 18 | select <- function(.x, ..., .strict = TRUE) { 19 | pos <- eval_select(rlang::expr(c(...)), .x, strict = .strict) 20 | rlang::set_names(.x[pos], names(pos)) 21 | } 22 | 23 | # Better printing 24 | mtcars <- tibble::as_tibble(mtcars) 25 | iris <- tibble::as_tibble(iris) 26 | options( 27 | tibble.print_min = 4, 28 | tibble.print_max = 4 29 | ) 30 | 31 | # Fix Latex error 32 | options( 33 | cli.unicode = FALSE 34 | ) 35 | ``` 36 | -------------------------------------------------------------------------------- /man/one_of.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/helpers.R 3 | \name{one_of} 4 | \alias{one_of} 5 | \title{Superseded selection helpers} 6 | \usage{ 7 | one_of(..., .vars = NULL) 8 | } 9 | \arguments{ 10 | \item{...}{One or more character vectors.} 11 | 12 | \item{.vars}{A character vector of variable names. When called 13 | from inside selecting functions like \code{\link[dplyr:select]{dplyr::select()}} these are 14 | automatically set to the names of the table.} 15 | } 16 | \description{ 17 | \code{one_of()} is superseded in favour of the more precise \code{\link[=any_of]{any_of()}} and 18 | \code{\link[=all_of]{all_of()}} selectors. 19 | } 20 | \keyword{internal} 21 | -------------------------------------------------------------------------------- /man/peek_vars.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vars.R 3 | \name{peek_vars} 4 | \alias{peek_vars} 5 | \alias{peek_data} 6 | \title{Peek at variables in the selection context} 7 | \usage{ 8 | peek_vars(..., fn = NULL) 9 | 10 | peek_data(..., fn = NULL) 11 | } 12 | \arguments{ 13 | \item{...}{These dots are for future extensions and must be empty.} 14 | 15 | \item{fn}{The name of the function to use in error messages when 16 | the helper is used in the wrong context. If not supplied, a 17 | generic error message is used instead.} 18 | } 19 | \description{ 20 | \itemize{ 21 | \item \code{peek_vars()} returns the vector of names of the variables 22 | currently available for selection. 23 | \item \code{peek_data()} returns the whole input vector (only available with 24 | \code{\link[=eval_select]{eval_select()}}). 25 | } 26 | 27 | Read the \href{https://tidyselect.r-lib.org/articles/tidyselect.html}{Get started} for 28 | examples of how to create selection helpers with \code{peek_vars()}. 29 | 30 | The variable names in a selection context are registered 31 | automatically by \code{\link[=eval_select]{eval_select()}} and \code{\link[=eval_rename]{eval_rename()}} for the 32 | duration of the evaluation. \code{peek_vars()} is the glue that connects 33 | \link[=language]{selection helpers} to the current selection 34 | context. 35 | } 36 | -------------------------------------------------------------------------------- /man/poke_vars.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vars.R 3 | \name{poke_vars} 4 | \alias{poke_vars} 5 | \alias{scoped_vars} 6 | \alias{with_vars} 7 | \alias{has_vars} 8 | \title{Replace or get current variables} 9 | \usage{ 10 | poke_vars(vars) 11 | 12 | scoped_vars(vars, frame = caller_env()) 13 | 14 | with_vars(vars, expr) 15 | 16 | has_vars() 17 | } 18 | \arguments{ 19 | \item{vars}{A character vector of variable names.} 20 | 21 | \item{frame}{The frame environment where the exit hook for 22 | restoring the old variables should be registered.} 23 | 24 | \item{expr}{An expression to be evaluated within the variable 25 | context.} 26 | } 27 | \value{ 28 | For \code{poke_vars()} and \code{scoped_vars()}, the old variables 29 | invisibly. For \code{peek_vars()}, the variables currently 30 | registered. 31 | } 32 | \description{ 33 | Variables are made available to \link[=language]{select helpers} by 34 | registering them in a special placeholder. 35 | \itemize{ 36 | \item \code{scoped_vars()} changes the current variables and sets up a 37 | function exit hook that automatically restores the previous 38 | variables once the current function returns. 39 | \item \code{with_vars()} takes an expression to be evaluated in a variable 40 | context. 41 | \item \code{poke_vars()} changes the contents of the placeholder with a new 42 | set of variables. It returns the previous variables invisibly and 43 | it is your responsibility to restore them after you are 44 | done. This is for expert use only. 45 | \item \code{peek_vars()} returns the variables currently registered. 46 | \item \code{has_vars()} returns \code{TRUE} if a variable context has been set, 47 | \code{FALSE} otherwise. 48 | } 49 | } 50 | \examples{ 51 | poke_vars(letters) 52 | peek_vars() 53 | 54 | # Now that the variables are registered, the helpers can figure out 55 | # the locations of elements within the variable vector: 56 | all_of(c("d", "z")) 57 | 58 | # In a function be sure to restore the previous variables. An exit 59 | # hook is the best way to do it: 60 | fn <- function(vars) { 61 | old <- poke_vars(vars) 62 | on.exit(poke_vars(old)) 63 | 64 | all_of("d") 65 | } 66 | fn(letters) 67 | fn(letters[3:5]) 68 | 69 | # The previous variables are still registered after fn() was 70 | # called: 71 | peek_vars() 72 | 73 | 74 | # It is recommended to use the scoped variant as it restores the 75 | # state automatically when the function returns: 76 | fn <- function(vars) { 77 | scoped_vars(vars) 78 | starts_with("r") 79 | } 80 | fn(c("red", "blue", "rose")) 81 | 82 | # The with_vars() helper makes it easy to pass an expression that 83 | # should be evaluated in a variable context. Thanks to lazy 84 | # evaluation, you can just pass the expression argument from your 85 | # wrapper to with_vars(): 86 | fn <- function(expr) { 87 | vars <- c("red", "blue", "rose") 88 | with_vars(vars, expr) 89 | } 90 | fn(starts_with("r")) 91 | } 92 | \seealso{ 93 | peek_vars 94 | } 95 | \keyword{internal} 96 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reexport-rlang.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{quo} 7 | \alias{quos} 8 | \alias{enquo} 9 | \alias{quo_name} 10 | \title{Objects exported from other packages} 11 | \keyword{internal} 12 | \description{ 13 | These objects are imported from other packages. Follow the links 14 | below to see their documentation. 15 | 16 | \describe{ 17 | \item{rlang}{\code{\link[rlang]{enquo}}, \code{\link[rlang:defusing-advanced]{quo}}, \code{\link[rlang:quo_label]{quo_name}}, \code{\link[rlang:defusing-advanced]{quos}}} 18 | }} 19 | 20 | -------------------------------------------------------------------------------- /man/rmd/overview.Rmd: -------------------------------------------------------------------------------- 1 | Tidyverse selections implement a dialect of R where operators make 2 | it easy to select variables: 3 | 4 | - `:` for selecting a range of consecutive variables. 5 | - `!` for taking the complement of a set of variables. 6 | - `&` and `|` for selecting the intersection or the union of two 7 | sets of variables. 8 | - `c()` for combining selections. 9 | 10 | In addition, you can use __selection helpers__. Some helpers select specific 11 | columns: 12 | 13 | * [`everything()`][tidyselect::everything]: Matches all variables. 14 | * [`last_col()`][tidyselect::last_col]: Select last variable, possibly with an offset. 15 | 16 | Other helpers select variables by matching patterns in their names: 17 | 18 | * [`starts_with()`][tidyselect::starts_with]: Starts with a prefix. 19 | * [`ends_with()`][tidyselect::ends_with()]: Ends with a suffix. 20 | * [`contains()`][tidyselect::contains()]: Contains a literal string. 21 | * [`matches()`][tidyselect::matches()]: Matches a regular expression. 22 | * [`num_range()`][tidyselect::num_range()]: Matches a numerical range like x01, x02, x03. 23 | 24 | Or from external variables stored in a character vector: 25 | 26 | * [`all_of()`][tidyselect::all_of()]: Matches variable names in a character vector. All 27 | names must be present, otherwise an out-of-bounds error is 28 | thrown. 29 | * [`any_of()`][tidyselect::any_of()]: Same as `all_of()`, except that no error is thrown 30 | for names that don't exist. 31 | 32 | Or using a predicate function: 33 | 34 | * [`where()`][tidyselect::where()]: Applies a function to all variables and selects those 35 | for which the function returns `TRUE`. 36 | -------------------------------------------------------------------------------- /man/rmd/setup.Rmd: -------------------------------------------------------------------------------- 1 | 2 | ```{r} 3 | #| include: FALSE 4 | 5 | options( 6 | tibble.print_min = 4, 7 | tibble.max_extra_cols = 8, 8 | digits = 2, 9 | crayon.enabled = FALSE, 10 | cli.unicode = FALSE 11 | ) 12 | 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>" 16 | ) 17 | 18 | library(tidyverse) 19 | ``` 20 | -------------------------------------------------------------------------------- /man/tidyselect-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tidyselect-package.R 3 | \docType{package} 4 | \name{tidyselect-package} 5 | \alias{tidyselect} 6 | \alias{tidyselect-package} 7 | \title{tidyselect: Select from a Set of Strings} 8 | \description{ 9 | A backend for the selecting functions of the 'tidyverse'. It makes it easy to implement select-like functions in your own packages in a way that is consistent with other 'tidyverse' interfaces for selection. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://tidyselect.r-lib.org} 15 | \item \url{https://github.com/r-lib/tidyselect} 16 | \item Report bugs at \url{https://github.com/r-lib/tidyselect/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Lionel Henry \email{lionel@posit.co} 22 | 23 | Authors: 24 | \itemize{ 25 | \item Hadley Wickham \email{hadley@posit.co} 26 | } 27 | 28 | Other contributors: 29 | \itemize{ 30 | \item Posit Software, PBC [copyright holder, funder] 31 | } 32 | 33 | } 34 | \keyword{internal} 35 | -------------------------------------------------------------------------------- /man/tidyselect_data_proxy.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/proxy.R 3 | \name{tidyselect_data_proxy} 4 | \alias{tidyselect_data_proxy} 5 | \alias{tidyselect_data_has_predicates} 6 | \title{tidyselect methods for custom types} 7 | \usage{ 8 | tidyselect_data_proxy(x) 9 | 10 | tidyselect_data_has_predicates(x) 11 | } 12 | \arguments{ 13 | \item{x}{A data-frame like object passed to \code{\link[=eval_select]{eval_select()}}, 14 | \code{\link[=eval_rename]{eval_rename()}}, and friends.} 15 | } 16 | \description{ 17 | \itemize{ 18 | \item \code{tidyselect_data_proxy()} returns a data frame. 19 | \item \code{tidyselect_data_has_predicates()} returns \code{TRUE} or \code{FALSE} 20 | } 21 | 22 | If your doesn't support predicate functions, return a 0-row data frame 23 | from \code{tidyselect_data_proxy()} and \code{FALSE} from 24 | \code{tidyselect_data_has_predicates()}. 25 | } 26 | -------------------------------------------------------------------------------- /man/vars_pull.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vars-pull.R 3 | \name{vars_pull} 4 | \alias{vars_pull} 5 | \title{Select variable} 6 | \usage{ 7 | vars_pull( 8 | vars, 9 | var = -1, 10 | error_call = caller_env(), 11 | error_arg = caller_arg(var) 12 | ) 13 | } 14 | \arguments{ 15 | \item{vars}{A character vector of existing column names.} 16 | 17 | \item{var}{A variable specified as: 18 | \itemize{ 19 | \item a literal variable name 20 | \item a positive integer, giving the position counting from the left 21 | \item a negative integer, giving the position counting from the right. 22 | } 23 | 24 | The default returns the last column (on the assumption that's the 25 | column you've created most recently). 26 | 27 | This argument is taken by expression and supports 28 | \link[rlang:topic-inject]{quasiquotation} (you can unquote column 29 | names and column locations).} 30 | 31 | \item{error_call}{The execution environment of a currently 32 | running function, e.g. \code{caller_env()}. The function will be 33 | mentioned in error messages as the source of the error. See the 34 | \code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.} 35 | 36 | \item{error_arg}{An argument name as a string. This argument 37 | will be mentioned in error messages as the input that is at the 38 | origin of a problem.} 39 | } 40 | \value{ 41 | The selected column name as an unnamed string. 42 | } 43 | \description{ 44 | This function powers \code{\link[dplyr:pull]{dplyr::pull()}} and various functions of the 45 | tidyr package. It is similar to \code{\link[=vars_select]{vars_select()}} but returns only 46 | one column name and has slightly different semantics: it allows 47 | negative numbers to select columns from the end. 48 | } 49 | \examples{ 50 | # It takes its argument by expression: 51 | vars_pull(letters, c) 52 | 53 | # Negative numbers select from the end: 54 | vars_pull(letters, -3) 55 | 56 | # You can unquote variables: 57 | var <- 10 58 | vars_pull(letters, !!var) 59 | } 60 | \seealso{ 61 | \code{\link[dplyr:pull]{dplyr::pull()}}, \code{\link[=vars_select]{vars_select()}} 62 | } 63 | \keyword{internal} 64 | -------------------------------------------------------------------------------- /man/vars_select.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/lifecycle-deprecated.R 3 | \name{vars_select} 4 | \alias{vars_select} 5 | \alias{vars_rename} 6 | \title{Select or rename variables} 7 | \usage{ 8 | vars_select( 9 | .vars, 10 | ..., 11 | .include = character(), 12 | .exclude = character(), 13 | .strict = TRUE 14 | ) 15 | 16 | vars_rename(.vars, ..., .strict = TRUE) 17 | } 18 | \arguments{ 19 | \item{.vars}{A character vector of existing column names.} 20 | 21 | \item{...}{Selection inputs. See the help for \link[=language]{selection helpers}.} 22 | 23 | \item{.include, .exclude}{Character vector of column names to always 24 | include/exclude.} 25 | 26 | \item{.strict}{If \code{TRUE}, will throw an error if you attempt to select or 27 | rename a variable that doesn't exist.} 28 | } 29 | \value{ 30 | A named character vector. Values are existing column names, 31 | names are new names. 32 | } 33 | \description{ 34 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#questioning}{\figure{lifecycle-questioning.svg}{options: alt='[Questioning]'}}}{\strong{[Questioning]}} 35 | 36 | Please use \code{\link[=eval_select]{eval_select()}} and \code{\link[=eval_rename]{eval_rename()}} instead. See 37 | \code{vignette("tidyselect")} to get started. 38 | } 39 | \seealso{ 40 | \code{\link[=vars_pull]{vars_pull()}} 41 | } 42 | \keyword{internal} 43 | -------------------------------------------------------------------------------- /man/vars_select_helpers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/zzz.R 3 | \docType{data} 4 | \name{vars_select_helpers} 5 | \alias{vars_select_helpers} 6 | \title{List of selection helpers} 7 | \format{ 8 | An object of class \code{list} of length 11. 9 | } 10 | \usage{ 11 | vars_select_helpers 12 | } 13 | \description{ 14 | This list contains all selection helpers exported in tidyselect. It 15 | was useful when you wanted to embed the helpers in your API without 16 | having to track addition of new helpers in tidyselect. However the 17 | selection helpers are now always embedded in the DSL. 18 | } 19 | \keyword{internal} 20 | -------------------------------------------------------------------------------- /man/where.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/helpers-where.R 3 | \name{where} 4 | \alias{where} 5 | \title{Select variables with a function} 6 | \usage{ 7 | where(fn) 8 | } 9 | \arguments{ 10 | \item{fn}{A function that returns \code{TRUE} or \code{FALSE} (technically, a 11 | \emph{predicate} function). Can also be a purrr-like formula.} 12 | } 13 | \description{ 14 | This \link[=language]{selection helper} selects the variables for which a 15 | function returns \code{TRUE}. 16 | } 17 | \section{Examples}{ 18 | 19 | 20 | Selection helpers can be used in functions like \code{dplyr::select()} 21 | or \code{tidyr::pivot_longer()}. Let's first attach the tidyverse: 22 | 23 | \if{html}{\out{
}}\preformatted{library(tidyverse) 24 | 25 | # For better printing 26 | iris <- as_tibble(iris) 27 | }\if{html}{\out{
}} 28 | 29 | \code{where()} takes a function and returns all variables for which the 30 | function returns \code{TRUE}: 31 | 32 | \if{html}{\out{
}}\preformatted{is.factor(iris[[4]]) 33 | #> [1] FALSE 34 | 35 | is.factor(iris[[5]]) 36 | #> [1] TRUE 37 | 38 | iris \%>\% select(where(is.factor)) 39 | #> # A tibble: 150 x 1 40 | #> Species 41 | #> 42 | #> 1 setosa 43 | #> 2 setosa 44 | #> 3 setosa 45 | #> 4 setosa 46 | #> # i 146 more rows 47 | 48 | is.numeric(iris[[4]]) 49 | #> [1] TRUE 50 | 51 | is.numeric(iris[[5]]) 52 | #> [1] FALSE 53 | 54 | iris \%>\% select(where(is.numeric)) 55 | #> # A tibble: 150 x 4 56 | #> Sepal.Length Sepal.Width Petal.Length Petal.Width 57 | #> 58 | #> 1 5.1 3.5 1.4 0.2 59 | #> 2 4.9 3 1.4 0.2 60 | #> 3 4.7 3.2 1.3 0.2 61 | #> 4 4.6 3.1 1.5 0.2 62 | #> # i 146 more rows 63 | }\if{html}{\out{
}} 64 | \subsection{The formula shorthand}{ 65 | 66 | You can use purrr-like formulas as a shortcut for creating a 67 | function on the spot. These expressions are equivalent: 68 | 69 | \if{html}{\out{
}}\preformatted{iris \%>\% select(where(is.numeric)) 70 | #> # A tibble: 150 x 4 71 | #> Sepal.Length Sepal.Width Petal.Length Petal.Width 72 | #> 73 | #> 1 5.1 3.5 1.4 0.2 74 | #> 2 4.9 3 1.4 0.2 75 | #> 3 4.7 3.2 1.3 0.2 76 | #> 4 4.6 3.1 1.5 0.2 77 | #> # i 146 more rows 78 | 79 | iris \%>\% select(where(function(x) is.numeric(x))) 80 | #> # A tibble: 150 x 4 81 | #> Sepal.Length Sepal.Width Petal.Length Petal.Width 82 | #> 83 | #> 1 5.1 3.5 1.4 0.2 84 | #> 2 4.9 3 1.4 0.2 85 | #> 3 4.7 3.2 1.3 0.2 86 | #> 4 4.6 3.1 1.5 0.2 87 | #> # i 146 more rows 88 | 89 | iris \%>\% select(where(~ is.numeric(.x))) 90 | #> # A tibble: 150 x 4 91 | #> Sepal.Length Sepal.Width Petal.Length Petal.Width 92 | #> 93 | #> 1 5.1 3.5 1.4 0.2 94 | #> 2 4.9 3 1.4 0.2 95 | #> 3 4.7 3.2 1.3 0.2 96 | #> 4 4.6 3.1 1.5 0.2 97 | #> # i 146 more rows 98 | }\if{html}{\out{
}} 99 | 100 | The shorthand is useful for adding logic inline. Here we select all 101 | numeric variables whose mean is greater than 3.5: 102 | 103 | \if{html}{\out{
}}\preformatted{iris \%>\% select(where(~ is.numeric(.x) && mean(.x) > 3.5)) 104 | #> # A tibble: 150 x 2 105 | #> Sepal.Length Petal.Length 106 | #> 107 | #> 1 5.1 1.4 108 | #> 2 4.9 1.4 109 | #> 3 4.7 1.3 110 | #> 4 4.6 1.5 111 | #> # i 146 more rows 112 | }\if{html}{\out{
}} 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | data.sqlite 2 | email* 3 | *noindex 4 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Revdeps 2 | 3 | ## Failed to check (34) 4 | 5 | |package |version |error |warning |note | 6 | |:------------------------|:-------|:-----|:-------|:----| 7 | |AlpsNMR |? | | | | 8 | |AnVIL |? | | | | 9 | |CellBench |? | | | | 10 | |CoSIA |? | | | | 11 | |curatedMetagenomicData |? | | | | 12 | |decoupleR |? | | | | 13 | |destiny |? | | | | 14 | |extraChIPs |? | | | | 15 | |fenr |? | | | | 16 | |GOSemSim |? | | | | 17 | |GRaNIE |? | | | | 18 | |imcRtools |? | | | | 19 | |iNETgrate |? | | | | 20 | |isoreader |? | | | | 21 | |jsontools |? | | | | 22 | |MicrobiotaProcess |? | | | | 23 | |mistyR |? | | | | 24 | |multicrispr |? | | | | 25 | |ngsReports |? | | | | 26 | |Omixer |? | | | | 27 | |OmnipathR |? | | | | 28 | |plyinteractions |? | | | | 29 | |plyranges |? | | | | 30 | |pmdplyr |? | | | | 31 | |qPLEXanalyzer |? | | | | 32 | |RnaSeqSampleSize |? | | | | 33 | |sccomp |? | | | | 34 | |ScreenR |? | | | | 35 | |SpatialCPie |? | | | | 36 | |Statial |? | | | | 37 | |tidybulk |? | | | | 38 | |tidySingleCellExperiment |? | | | | 39 | |tidySummarizedExperiment |? | | | | 40 | |triptych |0.1.2 |1 | | | 41 | 42 | ## New problems (2) 43 | 44 | |package |version |error |warning |note | 45 | |:----------|:-------|:------|:-------|:----| 46 | |[ipumsr](problems.md#ipumsr)|0.7.1 |__+1__ | | | 47 | |[pointblank](problems.md#pointblank)|0.12.0 |__+1__ | |1 | 48 | 49 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 485 reverse dependencies (452 from CRAN + 33 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 2 new problems 6 | * We failed to check 1 packages 7 | 8 | Issues with CRAN packages are summarised below. 9 | 10 | ### New problems 11 | (This reports the first line of each new failure) 12 | 13 | * ipumsr 14 | checking tests ... ERROR 15 | 16 | * pointblank 17 | checking tests ... ERROR 18 | 19 | ### Failed to check 20 | 21 | * triptych (NA) 22 | -------------------------------------------------------------------------------- /revdep/dplyr/README.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | |field |value | 4 | |:--------|:----------------------------| 5 | |version |R version 3.6.2 (2019-12-12) | 6 | |os |macOS Catalina 10.15.2 | 7 | |system |x86_64, darwin15.6.0 | 8 | |ui |X11 | 9 | |language |(EN) | 10 | |collate |en_US.UTF-8 | 11 | |ctype |en_US.UTF-8 | 12 | |tz |Europe/Brussels | 13 | |date |2020-01-10 | 14 | 15 | # Dependencies 16 | 17 | |package |old |new |Δ | 18 | |:----------|:-----|:-----------|:--| 19 | |tidyselect |0.2.5 |0.2.99.9000 |* | 20 | |digest |NA |0.6.23 |* | 21 | |purrr |NA |0.3.3 |* | 22 | |rlang |NA |0.4.2 |* | 23 | |vctrs |NA |0.2.99.9001 |* | 24 | 25 | # Revdeps 26 | 27 | ## Failed to check (50) 28 | 29 | |package |version |error |warning |note | 30 | |:--------------|:--------|:-----|:-------|:----| 31 | |airGR |1.3.2.42 |1 | | | 32 | |annotation |? | | | | 33 | |annotatr |? | | | | 34 | |bioCancer |? | | | | 35 | |BiocOncoTK |? | | | | 36 | |brunnermunzel |1.4.1 |1 | | | 37 | |CAGEWorkflow |? | | | | 38 | |Cardinal |2.2.6 |1 | | | 39 | |CATALYST |1.8.7 |2 | |2 | 40 | |ChIPseeker |? | | | | 41 | |cicero |1.2.0 |1 | |1 | 42 | |CINdex |? | | | | 43 | |clustermq |0.8.8.1 |1 | |1 | 44 | |codemetar |0.1.8 |1 | |1 | 45 | |corrcoverage |1.2.1 |1 | | | 46 | |DepthProc |2.1.2 |1 | | | 47 | |DiffBind |2.12.0 |1 |1 |4 | 48 | |downscaledl |1.0 |1 | | | 49 | |fastLink |0.5.0 |1 | | | 50 | |FSelectorRcpp |? | | | | 51 | |gap |1.2.1 |1 | | | 52 | |GAPGOM |? | | | | 53 | |gastempt |0.4.4 |1 | |2 -1 | 54 | |geneXtendeR |? | | | | 55 | |gQTLstats |1.16.0 |1 | |5 | 56 | |graphicalVAR |0.2.2 |1 | | | 57 | |grasp2db |1.1.0 |1 |3 |4 | 58 | |heatwaveR |0.4.2 |1 | | | 59 | |ICAMS |? | | | | 60 | |idefix |0.4.0 |1 | | | 61 | |imager |0.41.2 |1 | | | 62 | |iRF |2.0.0 |1 | | | 63 | |lpirfs |0.1.7 |1 | | | 64 | |miceFast |0.5.1 |1 | | | 65 | |MixMatrix |0.2.4 |1 | | | 66 | |PAST |1.0.1 |1 | |2 | 67 | |PathwaySplice |1.8.0 |1 | |2 | 68 | |poppr |2.8.3 |1 | | | 69 | |psychonetrics |0.4.1 |1 | | | 70 | |qgraph |1.6.4 |1 | | | 71 | |resautonet |1.0 |1 | | | 72 | |RforProteomics |? | | | | 73 | |rstap |1.0.3 |1 | |3 | 74 | |sarima |0.8.1 |1 | | | 75 | |scater |1.12.2 |1 | |1 | 76 | |simputation |0.2.3 |1 | | | 77 | |SpaDES.core |0.2.7 |1 | |1 | 78 | |TitanCNA |1.22.0 |1 |2 |4 | 79 | |weibulltools |1.0.1 |1 | | | 80 | |XBSeq |1.16.0 |1 | |2 | 81 | 82 | ## New problems (8) 83 | 84 | |package |version |error |warning |note | 85 | |:------------------------------------|:-------|:--------|:-------|:----| 86 | |[AMARETTO](problems.md#amaretto) |1.0.0 |1 __+1__ | |1 | 87 | |[assertr](problems.md#assertr) |2.6 |__+1__ | | | 88 | |[ddpcr](problems.md#ddpcr) |1.11 |__+2__ | | | 89 | |[disk.frame](problems.md#diskframe) |0.3.1 |__+1__ | | | 90 | |[levi](problems.md#levi) |1.2.0 |__+1__ | |1 | 91 | |[Mapinguari](problems.md#mapinguari) |1.0.0 |__+1__ | |1 | 92 | |[SemNeT](problems.md#semnet) |1.1.2 |__+1__ | |1 | 93 | |[teamcolors](problems.md#teamcolors) |0.0.3 |__+1__ | | | 94 | 95 | -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | # ipumsr 2 | 3 |
4 | 5 | * Version: 0.7.1 6 | * GitHub: https://github.com/ipums/ipumsr 7 | * Source code: https://github.com/cran/ipumsr 8 | * Date/Publication: 2024-02-26 16:00:03 UTC 9 | * Number of recursive dependencies: 138 10 | 11 | Run `revdepcheck::cloud_details(, "ipumsr")` for more info 12 | 13 |
14 | 15 | ## Newly broken 16 | 17 | * checking tests ... ERROR 18 | ``` 19 | Running ‘testthat.R’ 20 | Running the tests in ‘tests/testthat.R’ failed. 21 | Complete output: 22 | > library(testthat) 23 | > library(ipumsr) 24 | 25 | Attaching package: 'ipumsr' 26 | 27 | The following object is masked from 'package:testthat': 28 | 29 | ... 30 | 'test_viewer.R:37:3' [success] 31 | End test: attribute-less microdata doesn't error 32 | 33 | Start test: nhgis codebook doesn't error 34 | 'test_viewer.R:45:3' [warning] 35 | 'test_viewer.R:46:3' [success] 36 | End test: nhgis codebook doesn't error 37 | 38 | Error: Test failures 39 | Execution halted 40 | ``` 41 | 42 | # pointblank 43 | 44 |
45 | 46 | * Version: 0.12.0 47 | * GitHub: https://github.com/rstudio/pointblank 48 | * Source code: https://github.com/cran/pointblank 49 | * Date/Publication: 2024-03-01 08:30:02 UTC 50 | * Number of recursive dependencies: 135 51 | 52 | Run `revdepcheck::cloud_details(, "pointblank")` for more info 53 | 54 |
55 | 56 | ## Newly broken 57 | 58 | * checking tests ... ERROR 59 | ``` 60 | Running ‘testthat.R’ 61 | Running the tests in ‘tests/testthat.R’ failed. 62 | Complete output: 63 | > library(testthat) 64 | > library(pointblank) 65 | > library(dittodb) 66 | Loading required package: DBI 67 | > test_check("pointblank") 68 | [ FAIL 19 | WARN 0 | SKIP 0 | PASS 1540 ] 69 | 70 | ... 71 | 7. ├─base::tryCatch(...) 72 | 8. │ └─base (local) tryCatchList(expr, classes, parentenv, handlers) 73 | 9. │ └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]]) 74 | 10. │ └─base (local) doTryCatch(return(expr), name, parentenv, handler) 75 | 11. └─pointblank:::resolve_columns(...) 76 | 12. └─rlang::cnd_signal(rlang::error_cnd("resolve_eval_err", parent = out)) 77 | 78 | [ FAIL 19 | WARN 0 | SKIP 0 | PASS 1540 ] 79 | Error: Test failures 80 | Execution halted 81 | ``` 82 | 83 | ## In both 84 | 85 | * checking data for non-ASCII characters ... NOTE 86 | ``` 87 | Note: found 1 marked UTF-8 string 88 | ``` 89 | 90 | -------------------------------------------------------------------------------- /revdep/tidyr/README.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | |field |value | 4 | |:--------|:----------------------------| 5 | |version |R version 3.6.2 (2019-12-12) | 6 | |os |macOS Catalina 10.15.2 | 7 | |system |x86_64, darwin15.6.0 | 8 | |ui |X11 | 9 | |language |(EN) | 10 | |collate |en_US.UTF-8 | 11 | |ctype |en_US.UTF-8 | 12 | |tz |Europe/Brussels | 13 | |date |2020-01-10 | 14 | 15 | # Dependencies 16 | 17 | |package |old |new |Δ | 18 | |:----------|:-----|:-----------|:--| 19 | |tidyselect |0.2.5 |0.2.99.9000 |* | 20 | |digest |NA |0.6.23 |* | 21 | |purrr |NA |0.3.3 |* | 22 | |rlang |NA |0.4.2 |* | 23 | |vctrs |NA |0.2.99.9001 |* | 24 | 25 | # Revdeps 26 | 27 | ## Failed to check (21) 28 | 29 | |package |version |error |warning |note | 30 | |:----------------|:-------|:-----|:-------|:----| 31 | |anomalyDetection |0.2.5 |1 | | | 32 | |BgeeDB |2.10.0 |1 | |1 | 33 | |circumplex |0.3.4 |1 | | | 34 | |clusterProfiler |? | | | | 35 | |CNPBayes |1.13.5 |2 |3 |3 | 36 | |colorednoise |1.0.5 |1 | | | 37 | |dexter |1.0.2 |1 | | | 38 | |dynfrail |0.5.2 |1 | | | 39 | |ELMER |2.8.3 |1 |1 |2 | 40 | |maEndToEnd |? | | | | 41 | |MSstats |3.16.2 |1 |1 |1 | 42 | |phenofit |0.2.5-2 |1 | | | 43 | |psycho |? | | | | 44 | |quokar |0.1.0 |1 | | | 45 | |RxODE |0.9.1-8 |1 | |1 | 46 | |sesame |? | | | | 47 | |TCGAbiolinks |2.12.6 |1 | |3 | 48 | |TPP |3.12.0 |1 | |3 | 49 | |trialr |0.1.3 |1 | | | 50 | |vlad |0.2.0 |1 | | | 51 | |activPAL |? | | | | 52 | 53 | ## New problems (7) 54 | 55 | |package |version |error |warning |note | 56 | |:--------------------------------------|:-------|:---------|:-------|:----| 57 | |[DEP](problems.md#dep) |1.6.1 |-1 __+1__ | |1 | 58 | |[GerminaR](problems.md#germinar) |1.3 |__+1__ | |1 | 59 | |[irteQ](problems.md#irteq) |1.0.0 |__+1__ | |1 | 60 | |[janitor](problems.md#janitor) |1.2.0 |__+1__ | | | 61 | |[mnreadR](problems.md#mnreadr) |2.1.3 |__+1__ | | | 62 | |[nationwider](problems.md#nationwider) |1.0.0 |__+1__ | | | 63 | |[sjPlot](problems.md#sjplot) |2.8.1 |__+1__ | |1 | 64 | 65 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(tidyselect) 3 | 4 | test_check("tidyselect") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/eval-bool.md: -------------------------------------------------------------------------------- 1 | # boolean operators throw relevant errors 2 | 3 | Code 4 | # Unknown names 5 | select_loc(mtcars, foobar & contains("am")) 6 | Condition 7 | Error in `select_loc()`: 8 | ! Can't select columns that don't exist. 9 | x Column `foobar` doesn't exist. 10 | Code 11 | select_loc(mtcars, contains("am") | foobar) 12 | Condition 13 | Error in `select_loc()`: 14 | ! Can't select columns that don't exist. 15 | x Column `foobar` doesn't exist. 16 | Code 17 | # Empty intersection 18 | select_loc(mtcars, cyl & am) 19 | Condition 20 | Error in `select_loc()`: 21 | ! Can't take the intersection of two columns. 22 | i `cyl & am` is always an empty selection. 23 | Code 24 | # Symbol operands are evaluated in strict mode 25 | foo <- 1:2 26 | select_loc(iris, Species | foo) 27 | Condition 28 | Error in `select_loc()`: 29 | ! Can't select columns that don't exist. 30 | x Column `foo` doesn't exist. 31 | 32 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/eval-c.md: -------------------------------------------------------------------------------- 1 | # uniquely-named inputs can't rename duplicates 2 | 3 | Code 4 | names(df) 5 | Output 6 | [1] "a" "b" "a" 7 | Code 8 | select_loc(df, c(foo = a)) 9 | Condition 10 | Error in `select_loc()`: 11 | ! Can't rename duplicate variables to `foo`. 12 | 13 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/eval-relocate.md: -------------------------------------------------------------------------------- 1 | # can't supply both `before` and `after` 2 | 3 | Code 4 | relocate_loc(c(x = 1), before = 1, after = 1) 5 | Condition 6 | Error in `relocate_loc()`: 7 | ! Can't supply both `before` and `after`. 8 | 9 | --- 10 | 11 | Code 12 | relocate_loc(c(x = 1), before = 1, after = 1, before_arg = ".before", 13 | after_arg = ".after") 14 | Condition 15 | Error in `relocate_loc()`: 16 | ! Can't supply both `.before` and `.after`. 17 | 18 | # can't relocate with out-of-bounds variables by default 19 | 20 | Code 21 | relocate_loc(x, c) 22 | Condition 23 | Error in `relocate_loc()`: 24 | ! Can't relocate columns that don't exist. 25 | x Column `c` doesn't exist. 26 | Code 27 | relocate_loc(x, c(1, 3)) 28 | Condition 29 | Error in `relocate_loc()`: 30 | ! Can't relocate columns that don't exist. 31 | i Location 3 doesn't exist. 32 | i There are only 2 columns. 33 | Code 34 | relocate_loc(x, a, before = c) 35 | Condition 36 | Error in `relocate_loc()`: 37 | ! Can't select columns that don't exist. 38 | x Column `c` doesn't exist. 39 | Code 40 | relocate_loc(x, a, after = c) 41 | Condition 42 | Error in `relocate_loc()`: 43 | ! Can't select columns that don't exist. 44 | x Column `c` doesn't exist. 45 | 46 | # can relocate with out-of-bounds variables in `expr` if `strict = FALSE` 47 | 48 | Code 49 | relocate_loc(x, a, before = c, strict = FALSE) 50 | Condition 51 | Error in `relocate_loc()`: 52 | ! Can't select columns that don't exist. 53 | x Column `c` doesn't exist. 54 | Code 55 | relocate_loc(x, a, after = c, strict = FALSE) 56 | Condition 57 | Error in `relocate_loc()`: 58 | ! Can't select columns that don't exist. 59 | x Column `c` doesn't exist. 60 | 61 | # can forbid rename syntax 62 | 63 | Code 64 | relocate_loc(x, c(foo = b), allow_rename = FALSE) 65 | Condition 66 | Error in `relocate_loc()`: 67 | ! Can't rename variables in this context. 68 | Code 69 | relocate_loc(x, c(b, foo = b), allow_rename = FALSE) 70 | Condition 71 | Error in `relocate_loc()`: 72 | ! Can't rename variables in this context. 73 | Code 74 | relocate_loc(x, c(b, foo = b), allow_rename = FALSE, error_arg = "...") 75 | Condition 76 | Error in `relocate_loc()`: 77 | ! Can't rename variables in this context. 78 | i In argument: `...`. 79 | 80 | # can forbid empty selections 81 | 82 | Code 83 | relocate_loc(x, allow_empty = FALSE, error_arg = "...") 84 | Condition 85 | Error in `relocate_loc()`: 86 | ! `...` must select at least one column. 87 | Code 88 | relocate_loc(mtcars, integer(), allow_empty = FALSE) 89 | Condition 90 | Error in `relocate_loc()`: 91 | ! Must select at least one item. 92 | Code 93 | relocate_loc(mtcars, starts_with("z"), allow_empty = FALSE) 94 | Condition 95 | Error in `relocate_loc()`: 96 | ! Must select at least one item. 97 | 98 | --- 99 | 100 | Code 101 | relocate_loc(mtcars, before = integer(), allow_empty = FALSE) 102 | Condition 103 | Error in `relocate_loc()`: 104 | ! Must select at least one item. 105 | Code 106 | relocate_loc(mtcars, starts_with("z"), allow_empty = FALSE) 107 | Condition 108 | Error in `relocate_loc()`: 109 | ! Must select at least one item. 110 | 111 | # `before` and `after` forbid renaming 112 | 113 | Code 114 | relocate_loc(x, b, before = c(new = c)) 115 | Condition 116 | Error in `relocate_loc()`: 117 | ! Can't rename variables in this context. 118 | i In argument: `before`. 119 | Code 120 | relocate_loc(x, b, before = c(new = c), before_arg = ".before") 121 | Condition 122 | Error in `relocate_loc()`: 123 | ! Can't rename variables in this context. 124 | i In argument: `.before`. 125 | Code 126 | relocate_loc(x, b, after = c(new = c)) 127 | Condition 128 | Error in `relocate_loc()`: 129 | ! Can't rename variables in this context. 130 | i In argument: `after`. 131 | Code 132 | relocate_loc(x, b, after = c(new = c), after_arg = ".after") 133 | Condition 134 | Error in `relocate_loc()`: 135 | ! Can't rename variables in this context. 136 | i In argument: `.after`. 137 | 138 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/eval-rename.md: -------------------------------------------------------------------------------- 1 | # rename_loc() works with predicate functions 2 | 3 | Code 4 | rename_loc(x, where(is.numeric)) 5 | Condition 6 | Error in `rename_loc()`: 7 | ! All renaming inputs must be named. 8 | 9 | # rename_loc() throws helpful errors 10 | 11 | Code 12 | # Unnamed vector 13 | rename_loc(letters, c(foo = a)) 14 | Condition 15 | Error in `rename_loc()`: 16 | ! Can't rename an unnamed vector. 17 | Code 18 | # Duplicate names (FIXME) 19 | rename_loc(mtcars, c(foo = cyl, foo = disp)) 20 | Condition 21 | Error in `rename_loc()`: 22 | ! Names must be unique. 23 | x These names are duplicated: 24 | * "foo" at locations 1 and 2. 25 | Code 26 | # Unnamed inputs 27 | rename_loc(iris, Species) 28 | Condition 29 | Error in `rename_loc()`: 30 | ! All renaming inputs must be named. 31 | 32 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/eval-walk.md: -------------------------------------------------------------------------------- 1 | # scalar boolean operators fail informatively 2 | 3 | Code 4 | select_loc(letters2, starts_with("a") || ends_with("b")) 5 | Condition 6 | Error in `select_loc()`: 7 | ! Can't use scalar `||` in selections. 8 | i Do you need `|` instead? 9 | Code 10 | select_loc(letters2, starts_with("a") && ends_with("b")) 11 | Condition 12 | Error in `select_loc()`: 13 | ! Can't use scalar `&&` in selections. 14 | i Do you need `&` instead? 15 | 16 | # can't use `*` and `^` in data context 17 | 18 | Code 19 | select_loc(letters2, a * 2) 20 | Condition 21 | Error in `select_loc()`: 22 | ! Can't use arithmetic operator `*` in selection context. 23 | Code 24 | select_loc(letters2, a^2) 25 | Condition 26 | Error in `select_loc()`: 27 | ! Can't use arithmetic operator `^` in selection context. 28 | 29 | # symbol lookup outside data informs caller about better practice 30 | 31 | Code 32 | vars <- c("a", "b") 33 | select_loc(letters2, vars) 34 | Condition 35 | Warning: 36 | Using an external vector in selections was deprecated in tidyselect 1.1.0. 37 | i Please use `all_of()` or `any_of()` instead. 38 | i See . 39 | Output 40 | a b 41 | 1 2 42 | 43 | # can forbid use of predicates 44 | 45 | Code 46 | select_loc(iris, where(is.factor), allow_predicates = FALSE) 47 | Condition 48 | Error in `select_loc()`: 49 | ! This tidyselect interface doesn't support predicates. 50 | 51 | # selections provide informative errors 52 | 53 | Code 54 | # Foreign errors during evaluation 55 | select_loc(iris, eval_tidy(foobar)) 56 | Condition 57 | Error in `select_loc()`: 58 | i In argument: `eval_tidy(foobar)`. 59 | Caused by error: 60 | ! object 'foobar' not found 61 | 62 | # use of .data is deprecated 63 | 64 | Code 65 | x <- select_loc(x, .data$a) 66 | Condition 67 | Warning: 68 | Use of .data in tidyselect expressions was deprecated in tidyselect 1.2.0. 69 | i Please use `"a"` instead of `.data$a` 70 | 71 | --- 72 | 73 | Code 74 | x <- select_loc(x, .data[[var]]) 75 | Condition 76 | Warning: 77 | Use of .data in tidyselect expressions was deprecated in tidyselect 1.2.0. 78 | i Please use `all_of(var)` (or `any_of(var)`) instead of `.data[[var]]` 79 | 80 | --- 81 | 82 | Code 83 | x <- vars_pull("a", .data[[var]]) 84 | Condition 85 | Warning: 86 | Use of .data in tidyselect expressions was deprecated in tidyselect 1.2.0. 87 | i Please use `all_of(var)` (or `any_of(var)`) instead of `.data[[var]]` 88 | 89 | # eval_walk() warns when using a predicate without where() 90 | 91 | Code 92 | invisible(select_loc(iris, is_integer)) 93 | Condition 94 | Warning: 95 | Use of bare predicate functions was deprecated in tidyselect 1.1.0. 96 | i Please use `where()` to wrap predicate functions instead. 97 | Code 98 | invisible(select_loc(iris, is.numeric)) 99 | Condition 100 | Warning: 101 | Use of bare predicate functions was deprecated in tidyselect 1.1.0. 102 | i Please use `where()` to wrap predicate functions instead. 103 | Code 104 | invisible(select_loc(iris, isTRUE)) 105 | Condition 106 | Warning: 107 | Use of bare predicate functions was deprecated in tidyselect 1.1.0. 108 | i Please use `where()` to wrap predicate functions instead. 109 | Code 110 | # Warning is not repeated 111 | invisible(select_loc(iris, is_integer)) 112 | Condition 113 | Warning: 114 | Use of bare predicate functions was deprecated in tidyselect 1.1.0. 115 | i Please use `where()` to wrap predicate functions instead. 116 | 117 | # eval_walk() errors when formula shorthand are not wrapped 118 | 119 | Code 120 | select_loc(mtcars, ~ is.numeric(.x)) 121 | Condition 122 | Error in `select_loc()`: 123 | ! Formula shorthand must be wrapped in `where()`. 124 | 125 | # Bad 126 | data %>% select(~is.numeric(.x)) 127 | 128 | # Good 129 | data %>% select(where(~is.numeric(.x))) 130 | Code 131 | select_loc(mtcars, ~ is.numeric(.x) || is.factor(.x) || is.character(.x)) 132 | Condition 133 | Error in `select_loc()`: 134 | ! Formula shorthand must be wrapped in `where()`. 135 | 136 | # Bad 137 | data %>% select(~is.numeric(.x) || is.factor(.x) || is.character(.x)) 138 | 139 | # Good 140 | data %>% select(where(~is.numeric(.x) || is.factor(.x) || is.character(.x))) 141 | Code 142 | select_loc(mtcars, ~ is.numeric(.x) || is.factor(.x) || is.character(.x) || 143 | is.numeric(.x) || is.factor(.x) || is.character(.x)) 144 | Condition 145 | Error in `select_loc()`: 146 | ! Formula shorthand must be wrapped in `where()`. 147 | 148 | # Bad 149 | data %>% select(~...) 150 | 151 | # Good 152 | data %>% select(where(~...)) 153 | Code 154 | select_loc(mtcars, .data$"foo") 155 | Condition 156 | Error in `select_loc()`: 157 | ! The RHS of `.data$rhs` must be a symbol. 158 | 159 | # can forbid empty selection 160 | 161 | Code 162 | ensure_named(integer(), allow_empty = FALSE) 163 | Condition 164 | Error: 165 | ! Must select at least one item. 166 | Code 167 | ensure_named(integer(), allow_empty = FALSE, allow_rename = FALSE) 168 | Condition 169 | Error: 170 | ! Must select at least one item. 171 | 172 | # duplicate names are checked when literals are supplied (#346) 173 | 174 | Code 175 | select_loc(df, "x") 176 | Condition 177 | Error in `select_loc()`: 178 | ! Names must be unique. 179 | x These names are duplicated: 180 | * "x" at locations 1 and 2. 181 | Code 182 | select_loc(df, c("x")) 183 | Condition 184 | Error in `select_loc()`: 185 | ! Names must be unique. 186 | x These names are duplicated: 187 | * "x" at locations 1 and 2. 188 | Code 189 | select_loc(df, c(!!1:2)) 190 | Condition 191 | Error in `select_loc()`: 192 | ! Names must be unique. 193 | x These names are duplicated: 194 | * "x" at locations 1 and 2. 195 | Code 196 | select_loc(df, !!(1:2)) 197 | Condition 198 | Error in `select_loc()`: 199 | ! Names must be unique. 200 | x These names are duplicated: 201 | * "x" at locations 1 and 2. 202 | 203 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/helpers-misc.md: -------------------------------------------------------------------------------- 1 | # last_col() checks its inputs 2 | 3 | Code 4 | last_col(Inf, letters[1:3]) 5 | Condition 6 | Error in `last_col()`: 7 | ! `offset` (Inf) must be smaller than the number of columns (3). 8 | Code 9 | last_col(3, letters[1:3]) 10 | Condition 11 | Error in `last_col()`: 12 | ! `offset` (3) must be smaller than the number of columns (3). 13 | Code 14 | last_col(0, character()) 15 | Condition 16 | Error in `last_col()`: 17 | ! Can't select last column when input is empty. 18 | Code 19 | last_col(1:2, letters[1:3]) 20 | Condition 21 | Error in `last_col()`: 22 | ! `offset` must be a single integer, not an integer vector. 23 | 24 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/helpers-pattern.md: -------------------------------------------------------------------------------- 1 | # num_range recycles with tidyverse rules (#355) 2 | 3 | Code 4 | select_loc(vars, num_range(c("x", "y"), 1:3)) 5 | Condition 6 | Error in `select_loc()`: 7 | i In argument: `num_range(c("x", "y"), 1:3)`. 8 | Caused by error in `num_range()`: 9 | ! Can't recycle `prefix` (size 2) to match `range` (size 3). 10 | 11 | --- 12 | 13 | Code 14 | select_loc(vars, num_range(c("x", "y"), 1:2, c("_foo", "_bar", "_baz"))) 15 | Condition 16 | Error in `select_loc()`: 17 | i In argument: `num_range(c("x", "y"), 1:2, c("_foo", "_bar", "_baz"))`. 18 | Caused by error in `num_range()`: 19 | ! Can't recycle `prefix` (size 2) to match `suffix` (size 3). 20 | 21 | # matches() complains about bad stringr pattern usage 22 | 23 | Code 24 | matches(stringr::fixed("a"), perl = TRUE) 25 | Condition 26 | Error in `matches()`: 27 | ! `perl` not supported when `match` is a stringr pattern. 28 | Code 29 | matches(stringr::fixed("a"), ignore.case = TRUE) 30 | Condition 31 | Error in `matches()`: 32 | ! `ignore.case` not supported when `match` is a stringr pattern. 33 | Code 34 | matches(stringr::fixed(c("a", "b"))) 35 | Condition 36 | Error in `matches()`: 37 | ! stringr patterns must be length 1. 38 | 39 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/helpers-vector.md: -------------------------------------------------------------------------------- 1 | # `all_of()` fails even if `.strict` is FALSE 2 | 3 | Code 4 | select_loc(letters2, all_of(c("a", "bar", "c")), strict = FALSE) 5 | Condition 6 | Error in `select_loc()`: 7 | i In argument: `all_of(c("a", "bar", "c"))`. 8 | Caused by error in `all_of()`: 9 | ! Can't subset elements that don't exist. 10 | x Element `bar` doesn't exist. 11 | 12 | # all_of() and any_of() check their inputs 13 | 14 | Code 15 | select_loc(letters2, all_of(NA)) 16 | Condition 17 | Error in `select_loc()`: 18 | ! Selections can't have missing values. 19 | Code 20 | select_loc(letters2, any_of(NA)) 21 | Condition 22 | Error in `select_loc()`: 23 | ! Selections can't have missing values. 24 | Code 25 | select_loc(letters2, all_of(TRUE)) 26 | Condition 27 | Error in `select_loc()`: 28 | i In argument: `all_of(TRUE)`. 29 | Caused by error in `all_of()`: 30 | ! Can't subset elements. 31 | x Subscript must be numeric or character, not `TRUE`. 32 | Code 33 | select_loc(letters2, any_of(TRUE)) 34 | Condition 35 | Error in `select_loc()`: 36 | i In argument: `any_of(TRUE)`. 37 | Caused by error in `any_of()`: 38 | ! Can't subset elements. 39 | x Subscript must be numeric or character, not `TRUE`. 40 | Code 41 | select_loc(letters2, any_of(is.factor)) 42 | Condition 43 | Error in `select_loc()`: 44 | i In argument: `any_of(is.factor)`. 45 | Caused by error in `any_of()`: 46 | ! Can't subset elements. 47 | x Subscript must be numeric or character, not a function. 48 | Code 49 | select_loc(letters2, all_of(is.factor)) 50 | Condition 51 | Error in `select_loc()`: 52 | i In argument: `all_of(is.factor)`. 53 | Caused by error in `all_of()`: 54 | ! Can't subset elements. 55 | x Subscript must be numeric or character, not a function. 56 | 57 | # any_of() errors out of context 58 | 59 | Code 60 | any_of() 61 | Condition 62 | Error: 63 | ! `any_of()` must be used within a *selecting* function. 64 | i See for details. 65 | 66 | # all_of() is deprecated out of context (#269) 67 | 68 | Code 69 | out <- all_of("x") 70 | Condition 71 | Warning: 72 | Using `all_of()` outside of a selecting function was deprecated in tidyselect 1.2.0. 73 | i See details at 74 | 75 | # any_of generates informative error if ... not empty 76 | 77 | Code 78 | any_of("b", "c", "d") 79 | Condition 80 | Error in `any_of()`: 81 | ! `...` must be empty. 82 | i Did you forget `c()`? 83 | i The expected syntax is `any_of(c("a", "b"))`, not `any_of("a", "b")` 84 | 85 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/helpers-where.md: -------------------------------------------------------------------------------- 1 | # where() checks return values 2 | 3 | Code 4 | where(NA) 5 | Condition 6 | Error in `where()`: 7 | ! Can't convert `fn`, a logical vector, to a function. 8 | 9 | --- 10 | 11 | Code 12 | select_loc(iris, where(~NA)) 13 | Condition 14 | Error in `where()`: 15 | ! Predicate must return `TRUE` or `FALSE`, not `NA`. 16 | Code 17 | select_loc(iris, where(~1)) 18 | Condition 19 | Error in `where()`: 20 | ! Predicate must return `TRUE` or `FALSE`, not the number 1. 21 | Code 22 | select_loc(iris, where(mean)) 23 | Condition 24 | Error in `where()`: 25 | ! Predicate must return `TRUE` or `FALSE`, not the number 5.84. 26 | 27 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/helpers.md: -------------------------------------------------------------------------------- 1 | # one_of gives useful errors 2 | 3 | Code 4 | one_of(1L, .vars = c("x", "y")) 5 | Condition 6 | Error in `one_of()`: 7 | ! Input 1 must be a vector of column names, not the number 1. 8 | 9 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/lifecycle-deprecated.md: -------------------------------------------------------------------------------- 1 | # vars_select() type-checks inputs 2 | 3 | Code 4 | vars_select(letters, TRUE) 5 | Condition 6 | Error: 7 | ! Can't select columns with `TRUE`. 8 | x `TRUE` must be numeric or character, not `TRUE`. 9 | Code 10 | vars_select(letters, 2.5) 11 | Condition 12 | Error: 13 | ! Can't select columns with `2.5`. 14 | x Can't convert from `2.5` to due to loss of precision. 15 | Code 16 | vars_select(letters, structure(1:3, class = "tidysel_foobar")) 17 | Condition 18 | Error: 19 | ! Can't select columns with `structure(1:3, class = "tidysel_foobar")`. 20 | x `structure(1:3, class = "tidysel_foobar")` must be numeric or character, not a object. 21 | 22 | # vars_select() has consistent location errors 23 | 24 | Code 25 | # Bare names 26 | vars_select(letters, foo) 27 | Condition 28 | Error: 29 | ! Can't select columns that don't exist. 30 | x Column `foo` doesn't exist. 31 | Code 32 | vars_select(letters, -foo) 33 | Condition 34 | Error: 35 | ! Can't select columns that don't exist. 36 | x Column `foo` doesn't exist. 37 | Code 38 | # Names 39 | vars_select(letters, "foo") 40 | Condition 41 | Error: 42 | ! Can't select columns that don't exist. 43 | x Column `foo` doesn't exist. 44 | Code 45 | vars_select(letters, a:"foo") 46 | Condition 47 | Error: 48 | ! Can't select columns that don't exist. 49 | x Column `foo` doesn't exist. 50 | Code 51 | # Locations 52 | vars_select(letters, 30, 50, 100) 53 | Condition 54 | Error: 55 | ! Can't select columns past the end. 56 | i Locations 30, 50, and 100 don't exist. 57 | i There are only 26 columns. 58 | Code 59 | vars_select(letters, -100) 60 | Condition 61 | Error: 62 | ! Can't select columns past the end. 63 | i Location 100 doesn't exist. 64 | i There are only 26 columns. 65 | Code 66 | vars_select(letters, !100) 67 | Condition 68 | Error: 69 | ! Can't select columns past the end. 70 | i Location 100 doesn't exist. 71 | i There are only 26 columns. 72 | 73 | # when .strict = FALSE, vars_rename always succeeds 74 | 75 | Code 76 | vars_rename(c("a", "b"), d = e, .strict = TRUE) 77 | Condition 78 | Error: 79 | ! Can't rename columns that don't exist. 80 | x Column `e` doesn't exist. 81 | Code 82 | vars_rename(c("a", "b"), d = e, f = g, .strict = TRUE) 83 | Condition 84 | Error: 85 | ! Can't rename columns that don't exist. 86 | x Column `e` doesn't exist. 87 | Code 88 | vars_rename(c("a", "b"), d = "e", f = "g", .strict = TRUE) 89 | Condition 90 | Error: 91 | ! Can't rename columns that don't exist. 92 | x Column `e` doesn't exist. 93 | 94 | # vars_rename() disallows renaming to same column 95 | 96 | Code 97 | # New column 98 | vars_rename(c("a", "b", "c"), foo = a, foo = b) 99 | Condition 100 | Error: 101 | ! Names must be unique. 102 | x These names are duplicated: 103 | * "foo" at locations 1 and 2. 104 | Code 105 | # Existing column 106 | vars_rename(c("a", "b", "c"), c = a, c = b) 107 | Condition 108 | Error: 109 | ! Names must be unique. 110 | x These names are duplicated: 111 | * "c" at locations 1, 2, and 3. 112 | 113 | # vars_rename() disallows renaming to existing columns (#70) 114 | 115 | Code 116 | # One column 117 | vars_rename(c("a", "b", "c"), b = a) 118 | Condition 119 | Error: 120 | ! Names must be unique. 121 | x These names are duplicated: 122 | * "b" at locations 1 and 2. 123 | Code 124 | # Multiple columns 125 | vars_rename(c("a", "b", "c", "d"), c = a, d = b) 126 | Condition 127 | Error: 128 | ! Names must be unique. 129 | x These names are duplicated: 130 | * "c" at locations 1 and 3. 131 | * "d" at locations 2 and 4. 132 | Code 133 | # Overlapping rename with one duplicate column 134 | vars_rename(c("a", "b", "c"), b = a, c = b) 135 | Condition 136 | Error: 137 | ! Names must be unique. 138 | x These names are duplicated: 139 | * "c" at locations 2 and 3. 140 | 141 | # vars_rename() type-checks arguments 142 | 143 | Code 144 | vars_rename(letters, A = TRUE) 145 | Condition 146 | Error: 147 | ! Can't rename columns with `TRUE`. 148 | x `TRUE` must be numeric or character, not `TRUE`. 149 | Code 150 | vars_rename(letters, A = 1.5) 151 | Condition 152 | Error: 153 | ! Can't rename columns with `1.5`. 154 | x Can't convert from `1.5` to due to loss of precision. 155 | Code 156 | vars_rename(letters, A = list()) 157 | Condition 158 | Error: 159 | ! Can't rename columns with `list()`. 160 | x `list()` must be numeric or character, not an empty list. 161 | 162 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/proxy.md: -------------------------------------------------------------------------------- 1 | # eval_*() respects proxy settings 2 | 3 | Code 4 | eval_select(quote(where(is.numeric)), foo) 5 | Condition 6 | Error: 7 | ! This tidyselect interface doesn't support predicates. 8 | Code 9 | eval_rename(quote(c(x = where(is.numeric))), foo) 10 | Condition 11 | Error: 12 | ! This tidyselect interface doesn't support predicates. 13 | Code 14 | eval_relocate(quote(where(is.numeric)), foo) 15 | Condition 16 | Error: 17 | ! This tidyselect interface doesn't support predicates. 18 | Code 19 | eval_relocate(quote(x), before = quote(where(is.numeric)), foo) 20 | Condition 21 | Error: 22 | ! This tidyselect interface doesn't support predicates. 23 | Code 24 | eval_relocate(quote(x), after = quote(where(is.numeric)), foo) 25 | Condition 26 | Error: 27 | ! This tidyselect interface doesn't support predicates. 28 | 29 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/vars-pull.md: -------------------------------------------------------------------------------- 1 | # errors for bad inputs 2 | 3 | Code 4 | vars_pull(letters, character()) 5 | Condition 6 | Error: 7 | ! `character()` must select exactly one column. 8 | Code 9 | vars_pull(letters, c("a", "b")) 10 | Condition 11 | Error: 12 | ! `c("a", "b")` must select exactly one column. 13 | Code 14 | vars_pull(letters, !!c("a", "b")) 15 | Condition 16 | Error: 17 | ! `!!c("a", "b")` must select exactly one column. 18 | Code 19 | vars_pull(letters, aa) 20 | Condition 21 | Error: 22 | ! object 'aa' not found 23 | Code 24 | vars_pull(letters, 0) 25 | Condition 26 | Error: 27 | ! Can't extract column with `0`. 28 | x Subscript `0` must be a positive location, not 0. 29 | Code 30 | vars_pull(letters, 100) 31 | Condition 32 | Error: 33 | ! Can't extract columns past the end. 34 | i Location 100 doesn't exist. 35 | i There are only 26 columns. 36 | Code 37 | vars_pull(letters, -100) 38 | Condition 39 | Error: 40 | ! Can't extract columns past the end. 41 | i Location 100 doesn't exist. 42 | i There are only 26 columns. 43 | Code 44 | vars_pull(letters, -Inf) 45 | Condition 46 | Error: 47 | ! Can't extract column with `-Inf`. 48 | x Can't convert from `-Inf` to due to loss of precision. 49 | Code 50 | vars_pull(letters, TRUE) 51 | Condition 52 | Error: 53 | ! Can't extract column with `TRUE`. 54 | x `TRUE` must be numeric or character, not `TRUE`. 55 | Code 56 | vars_pull(letters, NA) 57 | Condition 58 | Error: 59 | ! Can't extract column with `NA`. 60 | x Subscript `NA` must be a location, not an integer `NA`. 61 | Code 62 | vars_pull(letters, na_int) 63 | Condition 64 | Error: 65 | ! Can't extract column with `na_int`. 66 | x Subscript `na_int` must be a location, not an integer `NA`. 67 | Code 68 | vars_pull(letters, "foo") 69 | Condition 70 | Error: 71 | ! Can't extract columns that don't exist. 72 | x Column `foo` doesn't exist. 73 | 74 | # gives informative error if quosure is missing 75 | 76 | Code 77 | f() 78 | Condition 79 | Error in `f()`: 80 | ! `var` is absent but must be supplied. 81 | 82 | # vars_pull() are base errors 83 | 84 | Code 85 | vars_pull(letters, foobar) 86 | Condition 87 | Error: 88 | ! object 'foobar' not found 89 | 90 | # vars_pull() errors mention correct calls 91 | 92 | Code 93 | vars_pull(letters, f()) 94 | Condition 95 | Error in `f()`: 96 | ! foo 97 | 98 | # vars_pull() produces correct backtraces 99 | 100 | Code 101 | print(expect_error(vars_pull(letters, f(base = TRUE)))) 102 | Output 103 | 104 | Error: 105 | i In argument: `f(base = TRUE)`. 106 | Caused by error in `h()`: 107 | ! foo 108 | --- 109 | Backtrace: 110 | x 111 | 1. +-base::print(expect_error(vars_pull(letters, f(base = TRUE)))) 112 | 2. +-testthat::expect_error(vars_pull(letters, f(base = TRUE))) 113 | 3. | \-testthat:::expect_condition_matching(...) 114 | 4. | \-testthat:::quasi_capture(...) 115 | 5. | +-testthat (local) .capture(...) 116 | 6. | | \-base::withCallingHandlers(...) 117 | 7. | \-rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo)) 118 | 8. +-tidyselect::vars_pull(letters, f(base = TRUE)) 119 | 9. | +-tidyselect:::with_chained_errors(...) 120 | 10. | | \-base::withCallingHandlers(...) 121 | 11. | \-rlang::eval_tidy(expr, set_names(seq_along(vars), vars)) 122 | 12. \-tidyselect (local) f(base = TRUE) 123 | 13. \-tidyselect (local) g(base) 124 | 14. \-tidyselect (local) h(base) 125 | 15. \-base::stop("foo") 126 | Code 127 | print(expect_error(vars_pull(letters, f(base = FALSE)))) 128 | Output 129 | 130 | Error: 131 | i In argument: `f(base = FALSE)`. 132 | Caused by error in `h()`: 133 | ! foo 134 | --- 135 | Backtrace: 136 | x 137 | 1. +-base::print(expect_error(vars_pull(letters, f(base = FALSE)))) 138 | 2. +-testthat::expect_error(vars_pull(letters, f(base = FALSE))) 139 | 3. | \-testthat:::expect_condition_matching(...) 140 | 4. | \-testthat:::quasi_capture(...) 141 | 5. | +-testthat (local) .capture(...) 142 | 6. | | \-base::withCallingHandlers(...) 143 | 7. | \-rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo)) 144 | 8. +-tidyselect::vars_pull(letters, f(base = FALSE)) 145 | 9. | +-tidyselect:::with_chained_errors(...) 146 | 10. | | \-base::withCallingHandlers(...) 147 | 11. | \-rlang::eval_tidy(expr, set_names(seq_along(vars), vars)) 148 | 12. \-tidyselect (local) f(base = FALSE) 149 | 13. \-tidyselect (local) g(base) 150 | 14. \-tidyselect (local) h(base) 151 | 152 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/vars.md: -------------------------------------------------------------------------------- 1 | # generic error message is thrown if `fn` is not supplied 2 | 3 | Code 4 | peek_vars() 5 | Condition 6 | Error: 7 | ! Selection helpers must be used within a *selecting* function. 8 | i See for details. 9 | 10 | --- 11 | 12 | Code 13 | peek_vars() 14 | Condition 15 | Error: 16 | ! Selection helpers must be used within a *selecting* function. 17 | i See ]8;package = tidyselect:topic = faq-selection-context;ide:help?tidyselect::faq-selection-context]8;; for details. 18 | 19 | -------------------------------------------------------------------------------- /tests/testthat/helper-tidyselect.R: -------------------------------------------------------------------------------- 1 | letters2 <- set_names(letters) 2 | -------------------------------------------------------------------------------- /tests/testthat/test-eval-bool.R: -------------------------------------------------------------------------------- 1 | test_that("can refer to columns in | operands", { 2 | expect_identical(select_loc(mtcars, cyl | am), c(cyl = 2L, am = 9L)) 3 | }) 4 | 5 | test_that("can refer to columns in & operands", { 6 | expect_identical(select_loc(mtcars, cyl & contains("am")), set_names(int(), chr())) 7 | expect_identical(select_loc(mtcars, cyl & where(is.numeric)), c(cyl = 2L)) 8 | }) 9 | 10 | test_that("can use named inputs in & operands", { 11 | x <- list(a = 1L, b = 2L) 12 | expect_identical(select_loc(x, a & c(foo = a)), c(foo = 1L)) 13 | expect_identical(select_loc(x, c(foo = a) & a), c(foo = 1L)) 14 | expect_identical(select_loc(x, c(foo = a) & c(bar = a)), named(int())) 15 | }) 16 | 17 | test_that("symbol operands are evaluated in strict mode", { 18 | foo <- 1:2 19 | expect_error( 20 | select(iris, Species | foo), 21 | class = "vctrs_error_subscript_oob" 22 | ) 23 | }) 24 | 25 | test_that("boolean operators throw relevant errors", { 26 | expect_error( 27 | select_loc(mtcars, foobar & contains("am")), 28 | class = "vctrs_error_subscript_oob" 29 | ) 30 | expect_error( 31 | select_loc(mtcars, contains("am") | foobar), 32 | class = "vctrs_error_subscript_oob" 33 | ) 34 | expect_error( 35 | select_loc(mtcars, cyl & am), 36 | "empty selection" 37 | ) 38 | 39 | expect_snapshot(error = TRUE, { 40 | "Unknown names" 41 | select_loc(mtcars, foobar & contains("am")) 42 | select_loc(mtcars, contains("am") | foobar) 43 | 44 | "Empty intersection" 45 | select_loc(mtcars, cyl & am) 46 | 47 | "Symbol operands are evaluated in strict mode" 48 | foo <- 1:2 49 | select_loc(iris, Species | foo) 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /tests/testthat/test-eval-c.R: -------------------------------------------------------------------------------- 1 | test_that("c() interpolates union and setdiff operations (#130)", { 2 | expect_identical(select_loc(mtcars, c(mpg:disp, -(mpg:cyl))), c(disp = 3L)) 3 | 4 | expect_identical(select_loc(mtcars, c(mpg, -mpg)), set_names(int(), chr())) 5 | expect_identical(select_loc(mtcars, c(mpg, -mpg, mpg)), c(mpg = 1L)) 6 | expect_identical(select_loc(mtcars, c(mpg, -mpg, mpg, -mpg)), set_names(int(), chr())) 7 | 8 | expect_identical(select_loc(mtcars, c(mpg, cyl, -mpg)), c(cyl = 2L)) 9 | expect_identical(select_loc(mtcars, c(mpg, cyl, -mpg, -cyl)), set_names(int(), chr())) 10 | expect_identical(select_loc(mtcars, c(mpg, cyl, -mpg, mpg, -cyl)), c(mpg = 1L)) 11 | }) 12 | 13 | test_that("c() expands dots", { 14 | fn <- function(...) select_loc(mtcars, c(...)) 15 | expect_identical(fn(), set_names(int(), chr())) 16 | expect_identical(fn(mpg), c(mpg = 1L)) 17 | expect_identical(fn(mpg, cyl), c(mpg = 1L, cyl = 2L)) 18 | expect_identical(fn(mpg, cyl, disp), c(mpg = 1L, cyl = 2L, disp = 3L)) 19 | }) 20 | 21 | test_that("c() combines names tidily", { 22 | expect_identical(select_loc(mtcars, c(foo = mpg)), set_names(1L, "foo")) 23 | expect_identical(select_loc(mtcars, c(foo = c(bar = mpg))), set_names(1L, "foo...bar")) 24 | 25 | expect_identical(select_loc(mtcars, c(foo = mpg:cyl)), set_names(1:2, c("foo1", "foo2"))) 26 | expect_identical(select_loc(mtcars, c(foo = c(bar = mpg:cyl))), set_names(1:2, c("foo...bar1", "foo...bar2"))) 27 | }) 28 | 29 | test_that("c() renames duplicates", { 30 | x <- list(a = 1L, b = 2L, a = 3L) 31 | expect_identical(select(x, foo = a, bar = b), list(foo = 1L, foo = 3L, bar = 2L)) 32 | }) 33 | 34 | test_that("allow named negative selections for consistency even if it has no effect", { 35 | expect_identical(select_loc(iris, c(foo = -!Species)), c(Species = 5L)) 36 | }) 37 | 38 | test_that("c() handles names consistently", { 39 | x <- list(a = 1L, b = 2L) 40 | expect_identical(select(x, a, foo = a), list(foo = 1L)) 41 | expect_identical(select(x, a, foo = a, bar = a), list(foo = 1L, bar = 1L)) 42 | expect_identical(select(x, foo = a, -a), named(list())) 43 | expect_identical(select(x, foo = a, -c(bar = a)), list(foo = 1L)) 44 | }) 45 | 46 | test_that("with uniquely-named inputs names are propagated with disambiguation", { 47 | expect_identical(select_loc(mtcars, c(foo = c(mpg, cyl))), c(foo1 = 1L, foo2 = 2L)) 48 | expect_identical(select_loc(mtcars, c(bar = c(foo = c(mpg, cyl)))), c(bar...foo1 = 1L, bar...foo2 = 2L)) 49 | }) 50 | 51 | test_that("with minimally-named inputs names are propagated without disambiguation", { 52 | expect_identical(select_loc(unclass(mtcars), c(foo = c(mpg, cyl))), c(foo = 1L, foo = 2L)) 53 | expect_identical(select_loc(unclass(mtcars), c(bar = c(foo = c(mpg, cyl)))), c(bar...foo = 1L, bar...foo = 2L)) 54 | }) 55 | 56 | test_that("uniquely-named inputs can't rename duplicates", { 57 | df <- vctrs::new_data_frame(list(a = 1, b = 2, a = 3)) 58 | 59 | expect_error(select_loc(df, c(foo = a)), "rename duplicate") 60 | expect_identical(select_loc(unclass(df), c(foo = a)), c(foo = 1L, foo = 3L)) 61 | 62 | expect_snapshot(error = TRUE, { 63 | names(df) 64 | select_loc(df, c(foo = a)) 65 | }) 66 | }) 67 | 68 | test_that("can select with c() (#2685)", { 69 | expect_identical(select_loc(letters2, c(a, z)), c(a = 1L, z = 26L)) 70 | }) 71 | 72 | test_that("unnegate() flattens quosures properly", { 73 | expr <- quo(-!!local(quo(am))) 74 | expect_identical( 75 | select_loc(mtcars, c(!!expr)), 76 | select_loc(mtcars, -am) 77 | ) 78 | }) 79 | 80 | test_that("`-x:-y` is syntax for `-(x:y)` for compatibility", { 81 | expect_identical( 82 | select_loc(iris, c(-1, -2:-3)), 83 | select_loc(iris, c(-1, -(2:3))) 84 | ) 85 | expect_identical( 86 | select_loc(iris, c(-Sepal.Length, -Sepal.Width:-Petal.Length)), 87 | select_loc(iris, c(-Sepal.Length, -(Sepal.Width:Petal.Length))) 88 | ) 89 | expect_identical( 90 | select_loc(iris, c(-1:-2)), 91 | select_loc(iris, -(1:2)) 92 | ) 93 | }) 94 | 95 | test_that("`c()` compacts missing arguments (#147)", { 96 | expect_identical(eval_select(quote(c(NULL, NULL)), mtcars), named(int())) 97 | expect_identical(eval_select(quote(c(, )), mtcars), named(int())) 98 | expect_identical(eval_select(quote(c(, 2, , )), mtcars), c(cyl = 2L)) 99 | }) 100 | -------------------------------------------------------------------------------- /tests/testthat/test-eval-relocate.R: -------------------------------------------------------------------------------- 1 | test_that("`before` and `after` relocates the selection", { 2 | x <- list(x = 1, a = "a", y = 2, b = "a") 3 | 4 | expect_identical( 5 | relocate_loc(x, x, after = y), 6 | c(a = 2L, y = 3L, x = 1L, b = 4L) 7 | ) 8 | expect_identical( 9 | relocate_loc(x, y, before = x), 10 | c(y = 3L, x = 1L, a = 2L, b = 4L) 11 | ) 12 | expect_identical( 13 | relocate_loc(x, where(is.character), before = x), 14 | c(a = 2L, b = 4L, x = 1L, y = 3L) 15 | ) 16 | }) 17 | 18 | test_that("works with `before` and `after` `everything()`", { 19 | x <- c(w = 1, x = 2, y = 3, z = 4) 20 | 21 | expect_identical( 22 | relocate_loc(x, c(y, z), before = everything()), 23 | c(y = 3L, z = 4L, w = 1L, x = 2L) 24 | ) 25 | expect_identical( 26 | relocate_loc(x, c(y, z), after = everything()), 27 | c(w = 1L, x = 2L, y = 3L, z = 4L) 28 | ) 29 | }) 30 | 31 | test_that("moves columns to the front when neither `before` nor `after` are specified", { 32 | x <- c(x = 1, y = 2, z = 3) 33 | 34 | expect_identical( 35 | relocate_loc(x, c(z, y)), 36 | c(z = 3L, y = 2L, x = 1L) 37 | ) 38 | }) 39 | 40 | test_that("empty `before` selection moves columns to front", { 41 | x <- c(x = 1, y = 2, z = 3) 42 | 43 | expect_identical( 44 | relocate_loc(x, y, before = where(is.character)), 45 | c(y = 2L, x = 1L, z = 3L) 46 | ) 47 | }) 48 | 49 | test_that("empty `after` selection moves columns to end", { 50 | x <- c(x = 1, y = 2, z = 3) 51 | 52 | expect_identical( 53 | relocate_loc(x, y, after = where(is.character)), 54 | c(x = 1L, z = 3L, y = 2L) 55 | ) 56 | }) 57 | 58 | test_that("minimum of the `before` selection is used", { 59 | x <- c(a = 1, b = 1, c = 1, d = 1, e = 1) 60 | expect_named(relocate_loc(x, e, before = c(b, d)), c("a", "e", "b", "c", "d")) 61 | }) 62 | 63 | test_that("maximum of the `after` selection is used", { 64 | x <- c(a = 1, b = 1, c = 1, d = 1, e = 1) 65 | expect_named(relocate_loc(x, b, after = c(a, c, e)), c("a", "c", "d", "e", "b")) 66 | }) 67 | 68 | test_that("works with zero column data frames (tidyverse/dplyr#6167)", { 69 | expect_identical(relocate_loc(data.frame(), any_of("b")), named(int())) 70 | expect_identical(relocate_loc(data.frame(), any_of("b"), before = where(is.character)), named(int())) 71 | expect_identical(relocate_loc(data.frame(), any_of("b"), after = where(is.character)), named(int())) 72 | }) 73 | 74 | test_that("retains the last duplicate when renaming while moving (tidyverse/dplyr#6209)", { 75 | # To enforce the invariant that relocating can't change the number of columns 76 | x <- c(x = 1) 77 | expect_identical( 78 | relocate_loc(x, c(a = x, b = x)), 79 | c(b = 1L) 80 | ) 81 | 82 | x <- c(x = 1, y = 2) 83 | expect_identical( 84 | relocate_loc(x, c(a = x, b = y, c = x)), 85 | c(b = 2L, c = 1L) 86 | ) 87 | }) 88 | 89 | test_that("respects order specified by `...` (tidyverse/dplyr#5328)", { 90 | x <- c(a = 1, x = 1, b = 1, z = 1, y = 1) 91 | 92 | expect_named( 93 | relocate_loc(x, c(x, y, z), before = x), 94 | c("a", "x", "y", "z", "b") 95 | ) 96 | expect_named( 97 | relocate_loc(x, c(x, y, z), after = last_col()), 98 | c("a", "b", "x", "y", "z") 99 | ) 100 | expect_named( 101 | relocate_loc(x, c(x, a, z)), 102 | c("x", "a", "z", "b", "y") 103 | ) 104 | }) 105 | 106 | test_that("allows for renaming (tidyverse/dplyr#5569)", { 107 | x <- c(a = 1, b = 1, c = 1) 108 | 109 | expect_named( 110 | relocate_loc(x, c(new = b)), 111 | c("new", "a", "c") 112 | ) 113 | expect_named( 114 | relocate_loc(x, c(new = b), after = c), 115 | c("a", "c", "new") 116 | ) 117 | }) 118 | 119 | test_that("can't supply both `before` and `after`", { 120 | expect_snapshot(error = TRUE, { 121 | relocate_loc(c(x = 1), before = 1, after = 1) 122 | }) 123 | expect_snapshot(error = TRUE, { 124 | relocate_loc(c(x = 1), before = 1, after = 1, before_arg = ".before", after_arg = ".after") 125 | }) 126 | }) 127 | 128 | test_that("can't relocate with out-of-bounds variables by default", { 129 | x <- c(a = 1, b = 2) 130 | 131 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 132 | relocate_loc(x, c) 133 | relocate_loc(x, c(1, 3)) 134 | relocate_loc(x, a, before = c) 135 | relocate_loc(x, a, after = c) 136 | }) 137 | }) 138 | 139 | test_that("can relocate with out-of-bounds variables in `expr` if `strict = FALSE`", { 140 | x <- c(a = 1, b = 2) 141 | 142 | expect_identical(relocate_loc(x, c, strict = FALSE), c(a = 1L, b = 2L)) 143 | expect_identical(relocate_loc(x, c(d = b, e = c), strict = FALSE), c(d = 2L, a = 1L)) 144 | 145 | # But still not with OOB variables in `before` or `after` 146 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 147 | relocate_loc(x, a, before = c, strict = FALSE) 148 | relocate_loc(x, a, after = c, strict = FALSE) 149 | }) 150 | }) 151 | 152 | test_that("accepts name spec", { 153 | x <- c(a = 1, b = 2, c = 3) 154 | 155 | expect_identical( 156 | relocate_loc(x, c(foo = c(c, a)), name_spec = "{outer}_{inner}"), 157 | c(foo_1 = 3L, foo_2 = 1L, b = 2L) 158 | ) 159 | expect_identical( 160 | relocate_loc(x, c(foo = c(bar = c, baz = a)), name_spec = "{outer}_{inner}"), 161 | c(foo_bar = 3L, foo_baz = 1L, b = 2L) 162 | ) 163 | }) 164 | 165 | test_that("can forbid rename syntax", { 166 | x <- c(a = 1, b = 2, c = 3) 167 | 168 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 169 | relocate_loc(x, c(foo = b), allow_rename = FALSE) 170 | relocate_loc(x, c(b, foo = b), allow_rename = FALSE) 171 | relocate_loc(x, c(b, foo = b), allow_rename = FALSE, error_arg = "...") 172 | }) 173 | 174 | expect_named(relocate_loc(x, c(c, b), allow_rename = FALSE), c("c", "b", "a")) 175 | }) 176 | 177 | test_that("can forbid empty selections", { 178 | x <- c(a = 1, b = 2, c = 3) 179 | 180 | expect_snapshot(error = TRUE, { 181 | relocate_loc(x, allow_empty = FALSE, error_arg = "...") 182 | 183 | relocate_loc(mtcars, integer(), allow_empty = FALSE) 184 | relocate_loc(mtcars, starts_with("z"), allow_empty = FALSE) 185 | }) 186 | }) 187 | 188 | test_that("can forbid empty selections", { 189 | x <- c(a = 1, b = 2, c = 3) 190 | 191 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 192 | relocate_loc(mtcars, before = integer(), allow_empty = FALSE) 193 | relocate_loc(mtcars, starts_with("z"), allow_empty = FALSE) 194 | }) 195 | }) 196 | 197 | 198 | test_that("`before` and `after` forbid renaming", { 199 | x <- c(a = 1, b = 2, c = 3) 200 | 201 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 202 | relocate_loc(x, b, before = c(new = c)) 203 | relocate_loc(x, b, before = c(new = c), before_arg = ".before") 204 | 205 | relocate_loc(x, b, after = c(new = c)) 206 | relocate_loc(x, b, after = c(new = c), after_arg = ".after") 207 | }) 208 | }) 209 | -------------------------------------------------------------------------------- /tests/testthat/test-eval-rename.R: -------------------------------------------------------------------------------- 1 | test_that("rename_loc() requires named vectors", { 2 | expect_error( 3 | rename_loc(letters, c(foo = a)), 4 | "unnamed vector" 5 | ) 6 | }) 7 | 8 | test_that("rename_loc() partially renames", { 9 | expect_identical( 10 | rename_loc(mtcars, c(foo = cyl, bar = disp)), 11 | int(foo = 2, bar = 3) 12 | ) 13 | }) 14 | 15 | test_that("rename_loc() allows renaming to self", { 16 | expect_identical( 17 | rename_loc(mtcars, c(mpg = mpg, cyl = cyl)), 18 | int(mpg = 1, cyl = 2) 19 | ) 20 | }) 21 | 22 | test_that("rename() always preserves order", { 23 | expect_identical( 24 | rename(mtcars, c(disp = disp, cyl = cyl, mpg = mpg)), 25 | mtcars 26 | ) 27 | }) 28 | 29 | test_that("rename_loc() partially renames", { 30 | expect_identical( 31 | rename_loc(mtcars, c(foo = cyl, bar = disp)), 32 | int(foo = 2, bar = 3) 33 | ) 34 | }) 35 | 36 | test_that("rename_loc() requires unique names", { 37 | expect_error( 38 | rename_loc(mtcars, c(foo = cyl, foo = disp)), 39 | class = "vctrs_error_names_must_be_unique" 40 | ) 41 | expect_error( 42 | rename_loc(mtcars, c(cyl = mpg, foo = disp)), 43 | class = "vctrs_error_names_must_be_unique" 44 | ) 45 | }) 46 | 47 | test_that("rename_loc() disambiguates if necessary", { 48 | expect_identical( 49 | rename_loc(mtcars, c(foo = starts_with("d"))), 50 | int(foo1 = 3, foo2 = 5) 51 | ) 52 | expect_identical( 53 | rename_loc(unclass(mtcars), c(foo = starts_with("d"))), 54 | int(foo = 3, foo = 5) 55 | ) 56 | }) 57 | 58 | test_that("rename_loc() allows renaming to existing variable that is also renamed", { 59 | expect_identical( 60 | rename_loc(mtcars, c(cyl = mpg, foo = cyl)), 61 | int(cyl = 1, foo = 2) 62 | ) 63 | }) 64 | 65 | test_that("rename_loc() allows fixing duplicates by locations", { 66 | dups <- vctrs::new_data_frame(list(x = 1, x = 2)) 67 | expect_identical( 68 | rename_loc(dups, c(foo = 2L)), 69 | int(foo = 2) 70 | ) 71 | }) 72 | 73 | test_that("rename_loc() works with predicate functions", { 74 | x <- data.frame(a = 1, b = 2, c = "x", stringsAsFactors = FALSE) 75 | expect_equal(rename_loc(x, c(var = where(is.numeric))), c(var1 = 1, var2 = 2)) 76 | expect_snapshot(rename_loc(x, where(is.numeric)), error = TRUE) 77 | }) 78 | 79 | test_that("rename_loc() requires named inputs", { 80 | expect_error(rename_loc(iris, Species), "named") 81 | expect_error(rename_loc(iris, c(contains("Width"))), "named") 82 | }) 83 | 84 | test_that("rename_loc() uses names inside c()", { 85 | expect_identical(rename_loc(iris, c(foo = Species)), c(foo = 5L)) 86 | }) 87 | 88 | test_that("rename_loc() throws helpful errors", { 89 | expect_snapshot(error = TRUE, { 90 | "Unnamed vector" 91 | rename_loc(letters, c(foo = a)) 92 | 93 | "Duplicate names (FIXME)" 94 | rename_loc(mtcars, c(foo = cyl, foo = disp)) 95 | 96 | "Unnamed inputs" 97 | rename_loc(iris, Species) 98 | }) 99 | }) 100 | -------------------------------------------------------------------------------- /tests/testthat/test-eval-select.R: -------------------------------------------------------------------------------- 1 | test_that("select() is generic", { 2 | expect_identical( 3 | select(set_names(letters), b:c), 4 | c(b = "b", c = "c") 5 | ) 6 | expect_identical( 7 | select(as.list(set_names(letters)), b:c), 8 | list(b = "b", c = "c") 9 | ) 10 | }) 11 | 12 | test_that("select() supports existing duplicates", { 13 | x <- list(a = 1, b = 2, a = 3) 14 | expect_identical(select(x, A = a), list(A = 1, A = 3)) 15 | }) 16 | 17 | test_that("absent `sel` leads to empty selection", { 18 | expect_identical(select_loc(mtcars), named(int())) 19 | expect_identical(select_loc(data.frame()), named(int())) 20 | }) 21 | 22 | test_that("zero-length vector `sel` leads to empty selection (#214)", { 23 | expect_identical(select_loc(mtcars, character()), named(int())) 24 | expect_identical(select_loc(data.frame(), character()), named(int())) 25 | }) 26 | 27 | test_that("can specify inclusion and exclusion", { 28 | x <- list(a = 1, b = 2, c = 3) 29 | expect_identical(select_loc(x, int(), include = "b"), c(b = 2L)) 30 | expect_identical(select_loc(x, -int(), exclude = c("a", "c")), c(b = 2L)) 31 | 32 | # Can exclude variables that don't exist 33 | expect_identical(select_loc(x, 2, exclude = "d"), c(b = 2L)) 34 | }) 35 | 36 | test_that("included variables added to front", { 37 | x <- list(a = 1, b = 2, c = 3) 38 | expect_named(select_loc(x, "a", include = "b"), c("b", "a")) 39 | expect_named(select_loc(x, "c", include = "b"), c("b", "c")) 40 | 41 | # but only if not already present 42 | expect_named(select_loc(x, c("a", "b"), include = "b"), c("a", "b")) 43 | expect_named(select_loc(x, c("b", "a"), include = "b"), c("b", "a")) 44 | 45 | state <- 0L 46 | fn <- fn <- function() { 47 | state <<- state + 1L 48 | "b" 49 | } 50 | 51 | select_loc(x, c("a", fn()), include = "b") 52 | expect_equal(state, 1) 53 | }) 54 | 55 | test_that("include and exclude validate their inputs", { 56 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 57 | x <- list(a = 1, b = 2, c = 3) 58 | select_loc(x, "a", include = 1) 59 | select_loc(x, "a", include = "d") 60 | select_loc(x, "a", exclude = 1) 61 | }) 62 | }) 63 | 64 | test_that("variables are excluded with non-strict `any_of()`", { 65 | expect_identical( 66 | select_loc(iris, 1:3, exclude = "foo"), 67 | select_loc(iris, 1:3) 68 | ) 69 | }) 70 | 71 | test_that("select_loc() checks inputs", { 72 | expect_error(select_loc(function() NULL), class = "vctrs_error_scalar_type") 73 | }) 74 | 75 | test_that("select_loc() accepts name spec", { 76 | expect_identical( 77 | select_loc(mtcars, c(foo = c(mpg, cyl)), name_spec = "{outer}_{inner}"), 78 | c(foo_1 = 1L, foo_2 = 2L) 79 | ) 80 | }) 81 | 82 | test_that("result is named even with constant inputs (#173)", { 83 | expect_identical( 84 | eval_select("Sepal.Width", iris), 85 | c(Sepal.Width = 2L) 86 | ) 87 | }) 88 | 89 | test_that("can forbid rename syntax (#178)", { 90 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 91 | select_loc(mtcars, c(foo = cyl), allow_rename = FALSE) 92 | select_loc(mtcars, c(cyl, foo = cyl), allow_rename = FALSE) 93 | select_loc(mtcars, c(cyl, foo = mpg), allow_rename = FALSE) 94 | select_loc(mtcars, c(foo = mpg, cyl), allow_rename = FALSE) 95 | select_loc(mtcars, c(foo = mpg, cyl), error_arg = "x", allow_rename = FALSE) 96 | }) 97 | 98 | expect_named(select_loc(mtcars, starts_with("c") | all_of("am"), allow_rename = FALSE), c("cyl", "carb", "am")) 99 | }) 100 | 101 | test_that("can forbid empty selections", { 102 | expect_snapshot(error = TRUE, { 103 | select_loc(mtcars, allow_empty = FALSE) 104 | select_loc(mtcars, integer(), allow_empty = FALSE) 105 | select_loc(mtcars, starts_with("z"), allow_empty = FALSE) 106 | }) 107 | }) 108 | 109 | test_that("can forbid empty selections with informative error", { 110 | expect_snapshot(error = TRUE, { 111 | select_loc(mtcars, allow_empty = FALSE, error_arg = "cols") 112 | select_loc(mtcars, integer(), allow_empty = FALSE, error_arg = "x") 113 | select_loc(mtcars, starts_with("z"), allow_empty = FALSE, error_arg = "y") 114 | }) 115 | }) 116 | 117 | test_that("eval_select() errors mention correct calls", { 118 | f <- function() stop("foo") 119 | expect_snapshot(select_loc(mtcars, f()), error = TRUE, cnd_class = TRUE) 120 | }) 121 | 122 | test_that("predicate outputs are type-checked", { 123 | expect_snapshot(select_loc(mtcars, function(x) ""), error = TRUE, cnd_class = TRUE) 124 | }) 125 | 126 | test_that("eval_select() produces correct backtraces", { 127 | f <- function(base) g(base) 128 | g <- function(base) h(base) 129 | h <- function(base) if (base) stop("foo") else abort("foo") 130 | 131 | local_options( 132 | rlang_trace_top_env = current_env(), 133 | rlang_trace_format_srcrefs = FALSE 134 | ) 135 | 136 | expect_snapshot({ 137 | print(expect_error(select_loc(mtcars, f(base = TRUE)))) 138 | print(expect_error(select_loc(mtcars, f(base = FALSE)))) 139 | }) 140 | }) 141 | 142 | test_that("eval_select() produces correct chained errors", { 143 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 144 | select_loc(mtcars, 1 + "") 145 | 146 | f <- function() 1 + "" 147 | select_loc(mtcars, f()) 148 | }) 149 | }) 150 | 151 | test_that("can select with predicate when `allow_rename` is `FALSE` (#225)", { 152 | sel <- eval_select( 153 | expr(where(is.numeric)), 154 | mtcars, 155 | allow_rename = FALSE 156 | ) 157 | expect_equal(sel, set_names(seq_along(mtcars), names(mtcars))) 158 | }) 159 | 160 | test_that("location must resolve to numeric variables throws error", { 161 | expect_error( 162 | select_loc(letters2, !!list()), 163 | class = "vctrs_error_subscript_type" 164 | ) 165 | expect_error( 166 | select_loc(letters2, !!env()), 167 | class = "vctrs_error_subscript_type" 168 | ) 169 | }) 170 | 171 | test_that("order is determined from inputs (#53)", { 172 | expect_identical( 173 | select_loc(mtcars, c(starts_with("c"), starts_with("d"))), 174 | c(cyl = 2L, carb = 11L, disp = 3L, drat = 5L) 175 | ) 176 | expect_identical( 177 | select_loc(mtcars, one_of(c("carb", "mpg"))), 178 | c(carb = 11L, mpg = 1L) 179 | ) 180 | }) 181 | 182 | 183 | test_that("middle (no-match) selector should not clear previous selectors (issue #2275)", { 184 | cn <- setNames(nm = c("x", "y", "z")) 185 | 186 | expect_equal( 187 | select_loc(cn, c(contains("x"), contains("foo"), contains("z"))), 188 | c(x = 1L, z = 3L) 189 | ) 190 | expect_equal( 191 | select_loc(cn, c(contains("x"), -contains("foo"), contains("z"))), 192 | c(x = 1L, z = 3L) 193 | ) 194 | }) 195 | 196 | test_that("selecting with predicate + rename doesn't duplicate", { 197 | x <- list(a = "x", b = "y", c = 1, d = 2) 198 | expect_equal(select_loc(x, c(where(is.numeric), C = c)), c(C = 3, d = 4)) 199 | expect_equal(select_loc(x, c(where(is.numeric), A = a)), c(c = 3, d = 4, A = 1)) 200 | }) 201 | -------------------------------------------------------------------------------- /tests/testthat/test-helpers-misc.R: -------------------------------------------------------------------------------- 1 | test_that("last_col() selects last argument with offset", { 2 | vars <- letters[1:3] 3 | expect_identical(last_col(0, vars), 3L) 4 | expect_identical(last_col(2, vars), 1L) 5 | }) 6 | 7 | test_that("last_col() checks its inputs", { 8 | expect_snapshot(error = TRUE, { 9 | last_col(Inf, letters[1:3]) 10 | last_col(3, letters[1:3]) 11 | last_col(0, character()) 12 | last_col(1:2, letters[1:3]) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /tests/testthat/test-helpers-vector.R: -------------------------------------------------------------------------------- 1 | test_that("`all_of()` fails even if `.strict` is FALSE", { 2 | expect_snapshot( 3 | error = TRUE, 4 | select_loc(letters2, all_of(c("a", "bar", "c")), strict = FALSE) 5 | ) 6 | }) 7 | 8 | test_that("any_of() and all_of() preserve order (#186)", { 9 | df <- data.frame(x = 1, y = 2) 10 | expect_identical(select_loc(df, any_of(c("y", "x"))), c(y = 2L, x = 1L)) 11 | expect_identical(select_loc(df, all_of(c("y", "x"))), c(y = 2L, x = 1L)) 12 | 13 | df <- data.frame(x = 1, y = 2, z = 3) 14 | expect_identical( 15 | select_loc(df, any_of(c("y", "z", "y", "x", "d", "z"))), 16 | c(y = 2L, z = 3L, x = 1L) 17 | ) 18 | }) 19 | 20 | test_that("all_of() and any_of() handle named vectors", { 21 | expect_identical(select_loc(letters2, all_of(c("a", foo = "b"))), c(a = 1L, foo = 2L)) 22 | expect_identical(select_loc(letters2, any_of(c("a", foo = "b", "bar"))), c(a = 1L, foo = 2L)) 23 | }) 24 | 25 | test_that("all_of() is strict", { 26 | expect_error(select_loc(letters2, all_of(c("a", "foo"))), class = "vctrs_error_subscript_oob") 27 | }) 28 | 29 | test_that("any_of() is lax", { 30 | expect_identical( 31 | select_loc(letters2, any_of(c("a", "foo"))), 32 | select_loc(letters2, a) 33 | ) 34 | expect_identical( 35 | select_loc(letters2, -any_of(c("a", "foo"))), 36 | select_loc(letters2, -a) 37 | ) 38 | }) 39 | 40 | test_that("all_of() and any_of() check their inputs", { 41 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 42 | select_loc(letters2, all_of(NA)) 43 | select_loc(letters2, any_of(NA)) 44 | 45 | select_loc(letters2, all_of(TRUE)) 46 | select_loc(letters2, any_of(TRUE)) 47 | 48 | select_loc(letters2, any_of(is.factor)) 49 | select_loc(letters2, all_of(is.factor)) 50 | }) 51 | }) 52 | 53 | test_that("any_of() errors out of context", { 54 | expect_snapshot(any_of(), error = TRUE, cnd_class = TRUE) 55 | }) 56 | 57 | test_that("all_of() is deprecated out of context (#269)", { 58 | expect_snapshot(out <- all_of("x")) 59 | expect_equal(out, "x") 60 | }) 61 | 62 | test_that("any_of generates informative error if ... not empty", { 63 | local_vars(letters) 64 | 65 | expect_snapshot(error = TRUE, { 66 | any_of("b", "c", "d") 67 | }) 68 | }) 69 | 70 | test_that("all_of() returns an integer vector", { 71 | with_vars( 72 | letters, 73 | expect_equal(all_of(c("b", "c", "d")), 2:4) 74 | ) 75 | }) 76 | -------------------------------------------------------------------------------- /tests/testthat/test-helpers-where.R: -------------------------------------------------------------------------------- 1 | test_that("where() selects with a predicate", { 2 | expect_identical(select_loc(iris, where(is.factor)), c(Species = 5L)) 3 | expect_identical(select_loc(iris, where(~ is.factor(.x))), c(Species = 5L)) 4 | }) 5 | 6 | test_that("where() checks return values", { 7 | expect_snapshot(error = TRUE, { 8 | where(NA) 9 | }) 10 | 11 | expect_snapshot(error = TRUE, { 12 | select_loc(iris, where(~NA)) 13 | select_loc(iris, where(~1)) 14 | select_loc(iris, where(mean)) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /tests/testthat/test-helpers.R: -------------------------------------------------------------------------------- 1 | test_that("one_of gives useful errors", { 2 | expect_snapshot(error = TRUE, cnd_class = TRUE, { 3 | one_of(1L, .vars = c("x", "y")) 4 | }) 5 | }) 6 | 7 | test_that("one_of tolerates but warns for unknown columns", { 8 | vars <- c("x", "y") 9 | 10 | expect_warning(res <- one_of("z", .vars = vars), "Unknown columns: `z`") 11 | expect_equal(res, integer(0)) 12 | expect_warning(res <- one_of(c("x", "z"), .vars = vars), "Unknown columns: `z`") 13 | expect_equal(res, 1L) 14 | }) 15 | 16 | test_that("one_of converts names to locations", { 17 | expect_equal(one_of("a", "z", .vars = letters), c(1L, 26L)) 18 | }) 19 | 20 | test_that("one_of works with variables", { 21 | var <- "x" 22 | expect_equal(select_loc(letters2, one_of(var)), c(x = 24L)) 23 | # error messages from rlang 24 | expect_error(select_loc(letters2, one_of(`_x`)), "not found") 25 | expect_error(select_loc(letters2, one_of(`_y`)), "not found") 26 | }) 27 | 28 | test_that("one_of works when passed variable name matches the column name (#2266)", { 29 | x <- "x" 30 | y <- "x" 31 | expect_equal(select_loc(letters2, one_of(!!x)), c(x = 24L)) 32 | expect_equal(select_loc(letters2, one_of(!!y)), c(x = 24L)) 33 | expect_equal(select_loc(letters2, one_of(y)), c(x = 24L)) 34 | }) 35 | 36 | test_that("one_of() supports S3 vectors", { 37 | expect_identical(select_loc(letters2, one_of(factor(c("a", "c")))), c(a = 1L, c = 3L)) 38 | }) 39 | 40 | test_that("one_of() compacts inputs (#110)", { 41 | letters_seq <- set_names(seq_along(letters2), letters2) 42 | expect_identical( 43 | select_loc(letters2, -one_of()), 44 | letters_seq 45 | ) 46 | expect_identical( 47 | select_loc(letters2, -one_of(NULL)), 48 | letters_seq 49 | ) 50 | }) 51 | 52 | test_that("no set variables throws error", { 53 | expect_error(one_of("z"), "`one_of()` must be used within a *selecting* function", fixed = TRUE) 54 | }) 55 | 56 | 57 | test_that("no set variables throws error from the correct function", { 58 | expect_error(one_of(starts_with("z")), "`one_of()` must be used within a *selecting* function", fixed = TRUE) 59 | }) 60 | -------------------------------------------------------------------------------- /tests/testthat/test-proxy.R: -------------------------------------------------------------------------------- 1 | test_that("eval_*() respects proxy settings", { 2 | foo <- structure(list(), class = "foo") 3 | local_bindings( 4 | tidyselect_data_proxy.foo = function(x) { 5 | data.frame(x = 1, y = 2) 6 | }, 7 | tidyselect_data_has_predicates.foo = function(x) { 8 | FALSE 9 | }, 10 | .env = globalenv() 11 | ) 12 | 13 | expect_equal(eval_relocate(quote(everything()), foo), c(x = 1, y = 2)) 14 | expect_equal(eval_select(quote(everything()), foo), c(x = 1, y = 2)) 15 | expect_equal(eval_rename(quote(c(x = everything())), foo), c(x1 = 1, x2 = 2)) 16 | 17 | expect_snapshot(error = TRUE, { 18 | eval_select(quote(where(is.numeric)), foo) 19 | eval_rename(quote(c(x = where(is.numeric))), foo) 20 | 21 | eval_relocate(quote(where(is.numeric)), foo) 22 | eval_relocate(quote(x), before = quote(where(is.numeric)), foo) 23 | eval_relocate(quote(x), after = quote(where(is.numeric)), foo) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /tests/testthat/test-sets.R: -------------------------------------------------------------------------------- 1 | test_that("`sel_union()` matches named elements", { 2 | expect_identical(sel_union(1L, c(foo = 1L)), c(foo = 1L)) 3 | expect_identical(sel_union(named(1L), c(foo = 1L)), c(foo = 1L)) 4 | expect_identical(sel_union(c(foo = 1L), 1L), c(foo = 1L)) 5 | expect_identical(sel_union(c(foo = 1L), named(1L)), c(foo = 1L)) 6 | 7 | expect_identical(sel_union(named(c(1L, 2L, 1L)), c(foo = 1L, 1L)), c(foo = 1L, 2L)) 8 | }) 9 | 10 | test_that("`sel_diff()` matches named elements", { 11 | expect_identical(sel_diff(named(1L), named(1L)), named(int())) 12 | expect_identical(sel_diff(named(1L), c(foo = 1L)), named(int())) 13 | expect_identical(sel_diff(named(1L), c(foo = 1L, bar = 1L)), named(int())) 14 | 15 | expect_identical(sel_diff(c(foo = 1L), named(1L)), named(int())) 16 | expect_identical(sel_diff(c(foo = 1L), c(foo = 1L)), named(int())) 17 | expect_identical(sel_diff(c(foo = 1L), c(bar = 1L)), c(foo = 1L)) 18 | 19 | expect_identical(sel_diff(c(foo = 1L), c(bar = 1L)), c(foo = 1L)) 20 | }) 21 | 22 | test_that("sel_intersect() matches named elements", { 23 | expect_identical(sel_intersect(1L, c(foo = 1L)), c(foo = 1L)) 24 | expect_identical(sel_intersect(c(foo = 1L), 1L), c(foo = 1L)) 25 | expect_identical(sel_intersect(c(foo = 1L), c(bar = 1L)), named(int())) 26 | }) 27 | 28 | test_that("sel_unique() returns unique elements", { 29 | expect_identical(sel_unique(c(1L, foo = 1L, bar = 1L)), c(foo = 1L, bar = 1L)) 30 | }) 31 | 32 | test_that("unique elements are returned", { 33 | expect_identical(sel_union(c(1L, 1L), 2L), c(1L, 2L)) 34 | expect_identical(sel_diff(c(1L, 1L), 2L), 1L) 35 | expect_identical(sel_intersect(c(foo = 1L), c(foo = 1L, foo = 1L)), c(foo = 1L)) 36 | }) 37 | 38 | test_that("order is preserved", { 39 | expect_identical(sel_union(c(1L, bar = 1L), c(1L, foo = 1L)), c(bar = 1L, foo = 1L)) 40 | expect_identical(sel_union(c(1L, foo = 1L), c(1L, bar = 1L)), c(foo = 1L, bar = 1L)) 41 | expect_identical(sel_union(c(1L, foo = 1L), c(1L, foo = 1L)), c(foo = 1L)) 42 | 43 | expect_identical(sel_intersect(c(1L, foo = 1L, bar = 1L), c(1L, bar = 1L, foo = 1L)), c(foo = 1L, bar = 1L)) 44 | expect_identical(sel_intersect(c(1L, bar = 1L, foo = 1L), c(1L, foo = 1L, bar = 1L)), c(bar = 1L, foo = 1L)) 45 | 46 | expect_identical(sel_diff(c(1L, foo = 1L, bar = 1L), c(1L, bar = 1L)), c(foo = 1L)) 47 | expect_identical(sel_diff(c(1L, bar = 1L, foo = 1L), c(1L, foo = 1L)), c(bar = 1L)) 48 | }) 49 | -------------------------------------------------------------------------------- /tests/testthat/test-vars-pull.R: -------------------------------------------------------------------------------- 1 | test_that("errors for bad inputs", { 2 | expect_error(vars_pull(letters, character()), "exactly one") 3 | expect_error(vars_pull(letters, c("a", "b")), "exactly one") 4 | expect_error(vars_pull(letters, !!c("a", "b")), "exactly one") 5 | 6 | # FIXME 7 | expect_error( 8 | vars_pull(letters, aa), 9 | "object 'aa' not found", 10 | fixed = TRUE 11 | ) 12 | expect_error( 13 | vars_pull(letters, "foo"), 14 | class = "vctrs_error_subscript_oob" 15 | ) 16 | 17 | expect_error( 18 | vars_pull(letters, 0), 19 | class = "vctrs_error_subscript_type" 20 | ) 21 | expect_error( 22 | vars_pull(letters, 100), 23 | class = "vctrs_error_subscript_oob" 24 | ) 25 | expect_error( 26 | vars_pull(letters, -100), 27 | class = "vctrs_error_subscript_oob" 28 | ) 29 | expect_error( 30 | vars_pull(letters, -Inf), 31 | class = "vctrs_error_subscript_type" 32 | ) 33 | 34 | expect_error( 35 | vars_pull(letters, TRUE), 36 | class = "vctrs_error_subscript_type" 37 | ) 38 | expect_error( 39 | vars_pull(letters, NA), 40 | class = "vctrs_error_subscript_type" 41 | ) 42 | expect_error( 43 | vars_pull(letters, na_int), 44 | class = "vctrs_error_subscript_type" 45 | ) 46 | 47 | 48 | expect_snapshot(error = TRUE, { 49 | vars_pull(letters, character()) 50 | vars_pull(letters, c("a", "b")) 51 | vars_pull(letters, !!c("a", "b")) 52 | 53 | vars_pull(letters, aa) 54 | vars_pull(letters, 0) 55 | vars_pull(letters, 100) 56 | vars_pull(letters, -100) 57 | vars_pull(letters, -Inf) 58 | vars_pull(letters, TRUE) 59 | vars_pull(letters, NA) 60 | vars_pull(letters, na_int) 61 | vars_pull(letters, "foo") 62 | }) 63 | }) 64 | 65 | test_that("gives informative error if quosure is missing", { 66 | f <- function(arg) vars_pull(letters, {{ arg }}) 67 | expect_snapshot(f(), error = TRUE) 68 | }) 69 | 70 | test_that("can pull variables with missing elements", { 71 | expect_identical(vars_pull(c("a", ""), a), "a") 72 | expect_identical(vars_pull(c("a", NA), a), "a") 73 | }) 74 | 75 | test_that("missing values are detected in vars_pull() (#72)", { 76 | lapply(list(NA_character_, NA_integer_, NA_real_, NA, NA_complex_), function(x) { 77 | expect_error(vars_pull(c("a", "b"), !!x), class = "vctrs_error_subscript_type") 78 | }) 79 | }) 80 | 81 | test_that("can pull with strings", { 82 | expect_identical(vars_pull(letters, "b"), vars_pull(letters, b)) 83 | expect_error(vars_pull(letters, "foo"), class = "vctrs_error_subscript_oob") 84 | 85 | # even if those strings are numbers (#200) 86 | vars <- c("-1", "0", "1") 87 | expect_equal(vars_pull(vars, "-1"), "-1") 88 | expect_equal(vars_pull(vars, "0"), "0") 89 | expect_equal(vars_pull(vars, "1"), "1") 90 | }) 91 | 92 | test_that("can pull with all_of() without warning", { 93 | expect_identical(vars_pull(letters, all_of("z")), "z") 94 | }) 95 | 96 | test_that("can pull with negative values", { 97 | expect_identical(vars_pull(letters, -1), "z") 98 | expect_identical(vars_pull(letters, -3), "x") 99 | }) 100 | 101 | test_that("vars_pull() are base errors", { 102 | expect_snapshot(vars_pull(letters, foobar), error = TRUE, cnd_class = TRUE) 103 | }) 104 | 105 | test_that("vars_pull() errors mention correct calls", { 106 | f <- function() stop("foo") 107 | expect_snapshot(vars_pull(letters, f()), error = TRUE, cnd_class = TRUE) 108 | }) 109 | 110 | test_that("vars_pull() produces correct backtraces", { 111 | f <- function(base) g(base) 112 | g <- function(base) h(base) 113 | h <- function(base) if (base) stop("foo") else abort("foo") 114 | 115 | local_options( 116 | rlang_trace_top_env = current_env(), 117 | rlang_trace_format_srcrefs = FALSE 118 | ) 119 | 120 | expect_snapshot({ 121 | print(expect_error(vars_pull(letters, f(base = TRUE)))) 122 | print(expect_error(vars_pull(letters, f(base = FALSE)))) 123 | }) 124 | }) 125 | -------------------------------------------------------------------------------- /tests/testthat/test-vars.R: -------------------------------------------------------------------------------- 1 | test_that("local_vars() restores previous state", { 2 | vars <- c("a", "b", "c") 3 | local_vars(vars) 4 | 5 | fn <- function() { 6 | local_vars(c("d", "e", "f")) 7 | starts_with("e") 8 | } 9 | expect_identical(fn(), 2L) 10 | 11 | expect_identical(peek_vars(), vars) 12 | }) 13 | 14 | test_that("with_vars() works", { 15 | vars <- c("a", "b", "c") 16 | local_vars(vars) 17 | 18 | fn <- function(expr) { 19 | with_vars(c("rose", "blue", "red"), expr) 20 | } 21 | expect_identical(fn(starts_with("r")), c(1L, 3L)) 22 | 23 | expect_identical(peek_vars(), vars) 24 | }) 25 | 26 | test_that("has_vars() detects variables", { 27 | expect_false(has_vars()) 28 | 29 | local_vars(letters) 30 | expect_true(has_vars()) 31 | }) 32 | 33 | test_that("Missing names are kept", { 34 | local_vars(c("foo", NA)) 35 | expect_identical(peek_vars(), c("foo", NA)) 36 | 37 | local_vars(c(NA, "foo")) 38 | expect_identical(peek_vars(), c(NA, "foo")) 39 | 40 | local_vars(c("bar", "")) 41 | expect_identical(peek_vars(), c("bar", "")) 42 | 43 | local_vars(c("", "bar")) 44 | expect_identical(peek_vars(), c("", "bar")) 45 | }) 46 | 47 | test_that("full data is in scope", { 48 | x <- structure(set_names(letters), class = c("foo", "character")) 49 | 50 | identical <- FALSE 51 | helper <- function(data = peek_data()) { 52 | identical <<- identical(data, x) 53 | 1 54 | } 55 | 56 | select_loc(x, helper()) 57 | expect_true(identical) 58 | }) 59 | 60 | test_that("peek_data() fails informatively", { 61 | expect_error(peek_data(), "must be used within") 62 | }) 63 | 64 | test_that("generic error message is thrown if `fn` is not supplied", { 65 | expect_snapshot(peek_vars(), error = TRUE) 66 | 67 | withr::local_options(cli.hyperlink = TRUE, rlang_interactive = TRUE) 68 | expect_snapshot(peek_vars(), error = TRUE) 69 | }) 70 | 71 | test_that("custom error message is thrown if `fn` is supplied", { 72 | expect_error(peek_vars(fn = "z"), "`z()` must be used", fixed = TRUE) 73 | }) 74 | -------------------------------------------------------------------------------- /tidyselect.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | --------------------------------------------------------------------------------