{"id":14066863,"url":"https://github.com/TimTeaFan/loopurrr","last_synced_at":"2025-07-29T23:32:16.453Z","repository":{"id":37284891,"uuid":"439307349","full_name":"TimTeaFan/loopurrr","owner":"TimTeaFan","description":"Translate purrr functions into regular for loops","archived":false,"fork":false,"pushed_at":"2025-07-23T01:04:02.000Z","size":4268,"stargazers_count":81,"open_issues_count":8,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-23T03:07:11.919Z","etag":null,"topics":["functional-programming","purrr","r"],"latest_commit_sha":null,"homepage":"https://timteafan.github.io/loopurrr/","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TimTeaFan.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"TimTeaFan","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2021-12-17T11:18:24.000Z","updated_at":"2025-03-22T10:38:45.000Z","dependencies_parsed_at":"2023-09-30T03:01:47.906Z","dependency_job_id":"8a1d7b07-530a-4a4a-9852-9f759bbb35a1","html_url":"https://github.com/TimTeaFan/loopurrr","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/TimTeaFan/loopurrr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTeaFan%2Floopurrr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTeaFan%2Floopurrr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTeaFan%2Floopurrr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTeaFan%2Floopurrr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TimTeaFan","download_url":"https://codeload.github.com/TimTeaFan/loopurrr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimTeaFan%2Floopurrr/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267780046,"owners_count":24143201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","purrr","r"],"created_at":"2024-08-13T07:05:18.259Z","updated_at":"2025-07-29T23:32:15.617Z","avatar_url":"https://github.com/TimTeaFan.png","language":"R","funding_links":["https://github.com/sponsors/TimTeaFan"],"categories":["R"],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n```\n\n# loopurrr\n\n\u003c!-- badges: start --\u003e\n![Release status](https://img.shields.io/badge/status-first%20release-yellow)\n[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)\n[![R-CMD-check](https://github.com/TimTeaFan/loopurrr/workflows/R-CMD-check/badge.svg)](https://github.com/TimTeaFan/loopurrr/actions)\n[![Codecov test coverage](https://codecov.io/gh/TimTeaFan/loopurrr/branch/main/graph/badge.svg)](https://codecov.io/gh/TimTeaFan/loopurrr?branch=main)\n[![CodeFactor](https://www.codefactor.io/repository/github/timteafan/loopurrr/badge)](https://www.codefactor.io/repository/github/timteafan/loopurrr)\n![CRAN status](https://img.shields.io/badge/CRAN-not%20published-red)\n[\u003cimg src=\"https://img.shields.io/badge/dynamic/json?url=https://raw.githubusercontent.com/TimTeaFan/loopurrr/twitter-like-badge/likes\u0026label=Likes\u0026query=$.data..public_metrics.like_count\u0026style=social\u0026logo=Twitter\"\u003e](https://twitter.com/timteafan/status/1511034067344572422?s=21)\n\u003c!-- badges: end --\u003e\n\n## Overview\n\n\u003cp id=\"logop\"\u003e\u003ca id=\"logo\" href=\"https://raw.githubusercontent.com/TimTeaFan/loopurrr/main/man/figures/logo_big.png\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/TimTeaFan/loopurrr/main/man/figures/logo.png\" alt=\"loopurrr's logo a cat sleeping in the form of a circle\" align=\"right\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n`{loopurrr}` makes `{purrr}`'s iterator functions more understandable and easier to debug. \nIn this initial version, `{loopurrr}` consists only of *one* main function: `as_loop()`.\n\n`as_loop()` takes a function call to one of `{purrr}`'s iterator functions, such as `purrr::map()`, and\ntranslates it into a regular `for` loop.\n\nYou might ask: *\"Why would anyone want to do this?!\"*\n\n`as_loop()` has at least three use cases:\n\n1. Learning and Teaching Functional Programming\n1. Debugging\n1. Accessing and Extending `{purrr}` Functions\n\nThe remainder of this readme will expand on the uses cases above, show how to get started, and give \na brief outlook on the development roadmap.\n\n## Motivation and Use Cases\n\n#### 1. Learning and Teaching Functional Programming\nBeginners, and especially users new to functional-style programming, often have a hard time getting\ntheir head around R's rather opaque iterator functions, such as base R's `lapply()` or `purrr::map()`.\n`for` loops, on the other hand, are fairly well understood, even by users new to R. \n\n`as_loop()` translates a function call to one of `{purrr}`'s iterator functions into a regular `for` loop.\nThrough this `as_loop()` shows how `{purrr}`'s iterator functions work under the hood. After reading about\nwhat iterator functions do (for example [here](https://r4ds.had.co.nz/iteration.html#the-map-functions)),\nLearneRs can start playing around with calling `as_loop()` on the examples in the `{purrr}` documentation.\nTeacheRs, on the other hand, can use `as_loop()` interactively when introducing the different types of\niterator functions in the `{purrr}` package.\n\nFinally, this package is not only for beginners and users new to R. When writing this package I was\nfairly confident in my understanding of `{purrr}`'s iterator functions. Nevertheless, translating \neach of them into a `for` loop was quite revealing, especially with the more complex functions, such as\n`purrr::reduce()` (specifically when the direction is set to `\"backward\"`).\n\n\n#### 2. Debugging\nOnce learneRs know what an iterator function does and how to use it, the next hurdle to take is dealing\nwith failure. Iterator functions introduce an additional layer of complexity, because special knowledge\nis required to debug non-running code (see also [here](https://r4ds.had.co.nz/iteration.html#dealing-with-failure)).\nBy translating an iterator function into a regular `for` loop, `as_loop()` can help with debugging.\nUsually a `for` loop will run over an index, for example `i`. When executed in the global environment,\nuseRs can easily inspect the code in the console at index `i` once the code throws an error -\nwithout any special knowledge of how to use a debugger, `browser()` or `purrr::safely()`.\n\nOf course, useRs are still highly encouraged to learn how to use R's and `{purrr}`'s debugging tools and \nfunctions. However, in data science teams with different levels of programming knowledge, the\npossibility to translate complex iterator functions to regular `for` loops can help mitigate \ntemporary knowledge gaps.\n\n\n#### 3. Accessing and Extending `{purrr}` Functions\nAfter getting used to `{purrr}`'s functions, they easily come to mind, when dealing with iteration\nproblems. However, sometimes the `{purrr}` package is not available, for example in a production\nenvironment where new packages cannot easily be installed, or when writing a package that doesn't \ndepend on `{purrr}`. Although base R equivalents exist for `{purrr}`'s major functions, there are \nfunctions like `purrr::imap()` or `purrr::reduce2()` which are not available in base R and need to\nbe constructed. In those cases, `as_loop()` provides a ready-to-use alternative.\n\nFurther, by translating `{purrr}`'s iterator functions into `for` loops, the underlying building blocks\ncan easily be rearranged to create functions that are not included in the `{purrr}` package. For example,\nby translating a call to `purrr::imap()` and a call to `purrr::map2()` we could easily build a `for`\nloop that loops over two vectors and an index, as if a function like `imap2()` existed.\n\n## Installation\n\n`{loopurrr}` is not on CRAN yet. You can install the latest version from \n[GitHub](https://github.com/TimTeaFan/loopurrr) with:\n\n```{r, eval = FALSE}\n# install.packages(\"remotes\")\nremotes::install_github(\"TimTeaFan/loopurrr\")\n```\n\n## Getting Started\n\nFirst, lets use `get_supported_fns(\"as_loop\")` to get a glimpse of which iterator functions from the\n`{purrr}` package are currently supported by `as_loop()`:\n\n```{r, comment = \"#\u003e\", collapse = TRUE, echo = TRUE, eval = TRUE, warning = FALSE, message = FALSE}\nlibrary(loopurrr)\nget_supported_fns(\"as_loop\")\n```\n\nNow lets take the first example of `{loopurrr}`'s documentation and start with translating a call to \n`purrr::map()`. First, lets look at the result: \n\n```{r, eval = TRUE}\nx \u003c- list(1, c(1:2), c(1:3))\nx %\u003e% purrr::map(sum)\n```\n\nNext, lets pipe the function call into `as_loop()`.\n```{r, eval = FALSE} \nx %\u003e% \n  purrr::map(sum) %\u003e% \n  as_loop()\n```\n\nDepending on the automatically detected output settings, the result will either be:\n\n1. directly *inserted in* the original R *script or the console*, given that the code is run in RStudio and the `{rstudioapi}` package is installed,\n1. *copied to the clipboard*, given that the above conditions are not met and the `{clipr}` package is installed,\n1. or if none of the conditions above are met, the result will just be *printed to the console*.\n\n```{r, eval = TRUE}\n# --- convert: `map(x, sum)` as loop --- #\nout \u003c- vector(\"list\", length = length(x))\n\nfor (i in seq_along(x)) {\n  out[[i]] \u003c- sum(x[[i]])\n}\n# --- end loop --- #\n```\n\nTo see the result we need to print `out`:\n\n```{r}\nout\n```\n\n## Roadmap and Collaboration\n\nFor future versions of `{loopurrr}` the following milestones are planned:\n\n- release `{loopurrr}` on CRAN\n- enable support for more iterator functions from `{purrr}` (e.g. `cross()` etc.)\n- support base R's `apply` family in `as_loop()`\n- translate `{purrr}`'s iterators to base R equivalents with `as_base()` (yet to be created)\n\nIf anyone is interested in collaborating on one or more of those milestones, any help is appreciated!\nFeel free to reach out to me, for example on Twitter \u003ca href=\"https://twitter.com/timteafan\" target=\"_blank\"\u003e@TimTeaFan\u003c/a\u003e.\n\n## History\n\nThe idea of this package is based on an experience I had at work. After diving deeper into `{purrr}`'s\niterator functions I started refactoring some old code by replacing loops with `map` functions. \nTo my surprise, although the code was much cleaner now, not everybody liked it. For some users it \nmade things harder to understand. Learning once how `map` functions work, was not enough to solve this,\nsince things got more complicated when the code was throwing errors. `{loopurrr}` allows us to \nwrite clean code and translate it to regular `for` loops when needed.\n\n## Acknowledgements\n\nCredit goes to the creators and maintainers of the amazing `{purrr}` package! \n`{loopurrr}` is just an add-on which would not exist without it.\n\nFurther, credit goes to the [`{gradethis}`](https://github.com/rstudio/gradethis/]) package from which I \nadapted [this code](https://github.com/rstudio/gradethis/blob/main/R/unpipe.R) to make `as_loop()` work\nwith piped expressions (function calls).\n[`{gradethis}`](https://github.com/rstudio/gradethis/blob/main/LICENSE.md) license and copyrights apply!\n\nI was further inpsired by Miles McBain's [`{datapasta}`](https://github.com/MilesMcBain/datapasta)'s \ndifferent output options. Looking at the code alone wasn't enough, I also got help on [StackOverflow](https://stackoverflow.com/questions/70572072/how-to-use-indentation-with-rstudioapiinserttext)\nfrom user @Waldi to make the {rstudioapi} package work.\n\nFinally, I adapted [this answer on StackOverflow](ttps://stackoverflow.com/a/33850689/9349302) to\nreplace the function arguments of the functions in `map(.f = )` with the actual objects that are being used.\n\n## Disclaimer\n\nThis package does not promote `for` loops over iterator functions. Rather the opposite is true. \nI love the `{purrr}` package and would be happy if people would use it more. \n\nAlthough this package contains tests with more than 1000 lines of code, there are definitely a\nnumber of edge cases which won't work correctly. If you find one, I'd be happy if you file an\nissue [here](https://github.com/TimTeaFan/loopurrr/issues).\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTimTeaFan%2Floopurrr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTimTeaFan%2Floopurrr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTimTeaFan%2Floopurrr/lists"}