{"id":19136837,"url":"https://github.com/mdub/lazily","last_synced_at":"2025-05-06T20:10:23.368Z","repository":{"id":8121518,"uuid":"9538142","full_name":"mdub/lazily","owner":"mdub","description":"Lazy Enumerables for everybody!","archived":false,"fork":false,"pushed_at":"2020-04-21T01:14:00.000Z","size":58,"stargazers_count":33,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-13T04:04:31.392Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdub.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-04-19T04:49:49.000Z","updated_at":"2021-12-16T03:17:38.000Z","dependencies_parsed_at":"2022-08-20T13:00:42.892Z","dependency_job_id":null,"html_url":"https://github.com/mdub/lazily","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdub%2Flazily","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdub%2Flazily/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdub%2Flazily/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdub%2Flazily/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdub","download_url":"https://codeload.github.com/mdub/lazily/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252761226,"owners_count":21800125,"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","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":[],"created_at":"2024-11-09T06:35:39.722Z","updated_at":"2025-05-06T20:10:23.339Z","avatar_url":"https://github.com/mdub.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"Lazily [![Build Status](https://secure.travis-ci.org/mdub/lazily.png?branch=master)](http://travis-ci.org/mdub/lazily)\n===========\n\nLazily provides various \"lazy\" Enumerable operations, similar to:\n\n* Clojure's sequences\n* Haskell's lists\n* Scala's \"views\"\n* Ruby 2.0's Enumerator::Lazy\n\nLazy filtering and transforming\n-------------------------------\n\nLazy evaluation is triggered using `Enumerable#lazily`:\n\n    [1,2,3].lazily              #=\u003e #\u003cLazily::Proxy: [1, 2, 3]\u003e\n\nThe resulting object implements lazy versions of various Enumerable methods.  Rather than returning Arrays, the lazy forms return new Enumerable objects, which generate results incrementally, on demand.\n\nConsider the following code, which squares a bunch of numbers, then takes the first 4 results.\n\n    \u003e\u003e (1..10).collect { |x| p x; x*x }.take(4)\n    1\n    2\n    3\n    4\n    5\n    6\n    7\n    8\n    9\n    10\n    =\u003e [1, 4, 9, 16]\n\nSee how it printed all the numbers from 1 to 10, indicating that the block given to `collect` was run ten times. We can do the same thing lazily, like this:\n\n    \u003e\u003e (1..10).lazily.collect { |x| p x; x*x }.take(4).to_a\n    1\n    2\n    3\n    4\n    =\u003e [1, 4, 9, 16]\n\nSame result, but notice how the block was only evaluated four times.\n\nLazy pipelines\n--------------\n\nBy combining two or more lazy operations, you can create an efficient \"pipeline\", e.g.\n\n    User.to_enum(:find_each).lazily.select do |u|\n      u.first_name[0] == u.last_name[0]\n    end.collect(\u0026:company).uniq.to_a\n\nIn case you missed it:\n\n    # enumerate all users\n    users = User.to_enum(:find_each)\n\n    # lazily select those matching \"X... X...\" (e.g. \"Mickey Mouse\")\n    users = users.lazily.select { |u| u.first_name[0] == u.last_name[0] }\n\n    # grab their company (weeding out duplicates)\n    companies = users.collect(\u0026:company).uniq\n\n    # force resolution\n    companies.to_a              #=\u003e [\"Disney\"]\n\nBecause the steps in the pipeline operate in parallel, without creation of intermediate collections (Arrays), you can efficiently operate on large (or even infinite) Enumerable collections.\n\nThis is analogous to a Unix shell pipeline, though of course here we're talking about streams of objects, rather than streams of text.\n\nLazy multi-threaded processing\n------------------------------\n\nThe `#in_threads` method is a multi-threaded version of `#collect`, allowing multiple elements of a collection to be processed in parallel.  It requires a numeric argument specifying the maximum number of Threads to use.\n\n    Benchmark.realtime do\n      [1,2,3,4].lazily.in_threads(10) do |x|\n        sleep 1\n        x * 2\n      end.to_a                  #=\u003e [2,4,6,8]\n    end.to_i                    #=\u003e 1\n\nOutputs will be yielded in the expected order, making it a drop-in replacement for `#collect`.\n\nUnlike some other \"parallel map\" implementations, the output of `#in_threads` is lazy (though it does need to pre-fetch elements from the source collection as required to start Threads).\n\nLazy queue processing\n---------------------\n\n`Lazily.dequeue` makes a Queue look like a (lazy) Enumerable, making it easier to process data produced by other Threads, e.g.\n\n    q = Queue.new\n    Thread.new do\n      q \u003c\u003c 1\n      q \u003c\u003c 2\n      q \u003c\u003c 3\n    end\n\n    Lazily.dequeue(q).take(2).to_a    # =\u003e [1,2]\n\nLazy combination of Enumerables\n-------------------------------\n\nLazily also provides some interesting ways to combine several Enumerable collections to create a new collection.\n\n`Lazily.zip` pulls elements from a number of collections in parallel, yielding each group.\n\n    array1 = [1,3,6]\n    array2 = [2,4,7]\n    Lazily.zip(array1, array2)          #=\u003e [1,2], [3,4], [6,7]\n\n`Lazily.concat` concatenates collections.\n\n    array1 = [1,3,6]\n    array2 = [2,4,7]\n    Lazily.concat(array1, array2)       #=\u003e [1,3,6,2,4,7]\n\n`Lazily.merge` merges multiple collections, preserving sort-order.  The inputs are assumed to be sorted already.\n\n    array1 = [1,4,5]\n    array2 = [2,3,6]\n    Lazily.merge(array1, array2)        #=\u003e [1,2,3,4,5,6]\n\nA block can be provided to determine the sort-order.\n\n    array1 = %w(a dd cccc)\n    array2 = %w(eee bbbbb)\n    Lazily.merge(array1, array2) { |x| x.length }\n                                        #=\u003e %w(a dd eee cccc bbbbb)\n\n`Lazily.associate` matches up \"like\" elements from separate collections.  Again, it assumes it's inputs sorted.\n\n    fruit = %w(apple banana orange)\n    nautical_terms = %w(anchor boat flag)\n    Lazily.associate(:fruit =\u003e fruit, :nautical =\u003e nautical_terms) do |word|\n      word.chars.first\n    end\n    #=\u003e [\n      { :fruit =\u003e [\"apple\"],  :nautical =\u003e [\"anchor\"] },\n      { :fruit =\u003e [\"banana\"], :nautical =\u003e [\"boat\"]   },\n      { :fruit =\u003e [],         :nautical =\u003e [\"flag\"]   },\n      { :fruit =\u003e [\"orange\"], :nautical =\u003e []         }\n    ]\n\nSame but different\n------------------\n\nThere are numerous similar implementations of lazy operations on Enumerables.\n\n### Lazily vs. Enumerating\n\nLazily supercedes \"[Enumerating](http://github.com/mdub/enumerating)\".  Whereas Enumerating mixed lazy operations directly onto `Enumerable`, Lazily does not.  Instead, it implements an API modelled on Ruby 2.0's `Enumerable#lazy`.\n\n### Lazily vs. Ruby 2.0\n\nQ: Why use Lazily, when Ruby 2.x has built-in lazy enumerations?\n\n- Compatibility: Perhaps you haven't managed to migrate to Ruby 2.0 yet.  Lazily provides the same benefits, but works in older versions of Ruby (most features work even in Ruby-1.8.7).\n- Consistency: Being pure-Ruby, you can use the same implementation of lazy enumeration across Ruby versions and interpreters.\n- Features: Lazily provides some extra features not present in Ruby 2.0, such as multi-threaded lazy enumeration.\n- Speed: Despite being implemented in pure Ruby, `Enumerable#lazily` actually performs a little better than `Enumerable#lazy`.\n\n### Others\n\nSee also:\n\n* Greg Spurrier's gem \"`lazing`\"\n* `Enumerable#defer` from the Ruby Facets library\n* The \"`backports`\" gem, which implements `Enumerable#lazy` for Ruby pre-2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdub%2Flazily","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdub%2Flazily","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdub%2Flazily/lists"}