Movatterモバイル変換


[0]ホーム

URL:


lazy.js0.5.1

Like Underscore, but lazier

Build StatusTweet+1

Lazy.js is a functional utility library for JavaScript, similar toUnderscore andLodash, but with alazy engine under the hood that strives to do as little work as possible while being as flexible as possible.

It has no external dependencies, so you can get started right away with:

npminstalllazy.js

(Note the package is called "lazy.js", with a dot.)

Or, if you're using Lazy.js in the browser:

<scripttype="text/javascript"src="lazy.js"></script><!-- optional: if you want support for DOM event and AJAX-based sequences: --><scripttype="text/javascript"src="lazy.browser.js"></script>

Now let's look at what you can do with Lazy.js. (For more thorough information, take a look at theAPI Docs.)

Introduction

Let's start with an array of objects representing people.

varpeople=getBigArrayOfPeople();

Suppose we're using this array to back some sort of search-as-you-type functionality, where users can search for people by their last names. Naturally we want to put some reasonable constraints on our problem space, so we'll provide up to 5 results at a time. Supposing the user types "Smith", we could therefore fetch results using something like this (using Underscore):

varresults=_.chain(people).pluck('lastName').filter(function(name){returnname.startsWith('Smith');}).take(5).value();

This query does a lot of stuff:

So if performance and/or efficiency were a concern for you, you would probablynot do things that way using Underscore. Instead, you'd likely go the procedural route:

varresults=[];for(vari=0;i<people.length;++i){if(people[i].lastName.startsWith('Smith')){results.push(people[i].lastName);if(results.length===5){break;}}}

There—now we haven't created any extraneous arrays, and we did all of the work in one iteration. Any problems?

Well, yeah. The main problem is that this is one-off code, which isn't reusable or particularly readable. If only we could somehow leverage the expressive power of Underscore but still get the performance of the hand-written procedural solution...


That's where Lazy.js comes in! Here's how we'd write the above query using Lazy.js:

varresult=Lazy(people).pluck('lastName').filter(function(name){returnname.startsWith('Smith');}).take(5);

Looks almost identical, right? That's the idea: Lazy.js aims to be completely familiar to JavaScript devs experienced with Underscore or Lodash. Every method from Underscore should have the same name and (almost) identical behavior in Lazy.js, except that instead of returning a fully-populated array on every call, it creates asequence object with aneach method.

What's important here is thatno iteration takes place until you calleach, andno intermediate arrays are created. Essentially Lazy.js combines all query operations into a "sequence" that behaves quite a bit like the procedural code we wrote a moment ago. (If you everdo want an array, simply calltoArray on the resulting sequence.)

Of course,unlike the procedural approach, Lazy.js lets you keep your code clean and functional, and focus on solving whatever problem you're actually trying to solve instead of optimizing array traversals.

Features

So, Lazy.js is basically Underscore with lazy evaluation. Is that it?

Nope!

Indefinite sequence generation

The sequence-based paradigm of Lazy.js lets you do some pretty cool things that simply aren't possible with Underscore's array-based approach. One of these is the generation ofindefinite sequences, which can go on forever, yet still support all of Lazy's built-in mapping and filtering capabilities.

Here's an example. Let's say we want 300 unique random numbers between 1 and 1000.

Lazy.generate(Math.random).map(function(e){returnMath.floor(e*1000)+1;}).uniq().take(300).each(function(e){console.log(e);});

Here's a slightly more advanced example: let's use Lazy.js to make aFibonacci sequence.

varfibonacci=Lazy.generate(function(){varx=1,y=1;returnfunction(){varprev=x;x=y;y+=prev;returnprev;};}());// Output: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]fibonacci.take(10).toArray();

OK, what else?

Asynchronous iteration

You've probablyseen code snippets before that show how to iterate over an array asynchronously in JavaScript. But have you seen an example with functional goodness like this?

Lazy.generate(Lazy.identity).async(1000)// specifies a 1-second interval between each element.map(function(x){returnString.fromCharCode(x+65);}).take(26).each(function(char){console.log(char);});

All right... what else?

Event sequences

With indefinite sequences, we saw that unlike Underscore and Lodash, Lazy.js doesn't actually need an in-memory collection to iterate over. And asynchronous sequences demonstrate that it also doesn't need to do all its iteration at once.

Now here's a really cool combination of these two features: with a small extension to Lazy.js (lazy.browser.js, a separate file to include in browser-based environments), you can apply all of the power of Lazy.js tohandling DOM events. In other words, Lazy.js lets you think of DOM events as asequence—just like any other—and apply the usualmap,filter, etc. functions on that sequence.

Here's an example. Let's say we want to handle allmousemove events on a given DOM element, and show their coordinates in one of two other DOM elements depending on location.

// First we define our "sequence" of events.varmouseEvents=Lazy(sourceElement).on("mousemove");// Map the Event objects to their coordinates, relative to the element.varcoordinates=mouseEvents.map(function(e){varelementRect=sourceElement.getBoundingClientRect();return[Math.floor(e.clientX-elementRect.left),Math.floor(e.clientY-elementRect.top)];});// For mouse events on one side of the element, display the coordinates in one place.coordinates.filter(function(pos){returnpos[0]<sourceElement.clientWidth/2;}).each(function(pos){displayCoordinates(leftElement,pos);});// For those on the other side, display them in a different place.coordinates.filter(function(pos){returnpos[0]>sourceElement.clientWidth/2;}).each(function(pos){displayCoordinates(rightElement,pos);});

Anything else? Of course!

String processing

Now here's something you may not have even thought of:String.match andString.split. In JavaScript, each of these methods returns anarray of substrings. If you think about it, this often means doing more work than necessary; but it's the quickest way (from a developer's standpoint) to get the job done.

For example, suppose you wanted the first five lines of a block of text. You could always do this:

varfirstFiveLines=text.split("\n").slice(0,5);

But of course, this actually splitsthe entire string into every single line. If the string is very large, this is quite wasteful.

With Lazy.js, we don't need to split up an entire string just to treat it as a sequence of lines. We can get the same effect by wrapping the string withLazy and callingsplit:

varfirstFiveLines=Lazy(text).split("\n").take(5);

This way we can read the first five lines of an arbitrarily large string (without pre-populating a huge array) and map/reduce on it just as with any other sequence.

Similarly withString.match: let's say we wanted to find the first 5 alphanumeric matches in a string. With Lazy.js, it's easy!

varfirstFiveWords=Lazy(text).match(/[a-z0-9]+/i).take(5);

Piece of cake.

Stream processing

Lazy.js can wrapstreams in Node.js as well.

Given anyReadable Stream, you can wrap it withLazy just as with arrays:

Lazy(stream).take(5)// Read just the first 5 chunks of data read into the buffer..each(processData);

For convenience, specialized helper methods for dealing with either file streams or HTTP streams are also offered. (Note: this API will probably change.)

// Read the first 5 lines from a file:Lazy.readFile("path/to/file").lines().take(5).each(doSomething);// Read lines 5-10 from an HTTP response.Lazy.makeHttpRequest("http://example.com").lines().drop(5).take(5).each(doSomething);

In each case, the elements in the sequence will be "chunks" of data most likely comprising multiple lines. Thelines() method splits each chunk into lines (lazily, of course).


This library is experimental and still a work in progress.

Select the tests you want to run below, choose either or(why?), and to compare Lazy.js, Underscore, and Lodash.

You can also choose to display results in,, or (which is generally the fastest of the popular JS utility libraries). Proportional results will likely be easier to read when you've selected many benchmarks.

Comparing the performance of Lazy.js to other libraries like Underscore and Lodash is unfortunately not black and white. On the one hand, callingtoArray on the result of a Lazy sequence will give you an actual JavaScript array, which you might need if, e.g., you're passing the result to a function from an external library. However, in most cases when you use methods likemap orfilter, you are probably just going to do something while iterating over the result. In this case, you don't need an array at all; callingeach on aLazy.Sequence will be functionally indistinguishable from calling_.each on an array.

Generally speaking, Lazy.js performs best (and "wins" more performance races) when youdon't have to calltoArray. However, when chaining methods together it's often still the fastest solution regardless.

Select:

TestUnderscoreLodashLazy.js

Select:

TestUnderscoreLodashLazy.js

Select:

TestUnderscoreLodashLazy.js
TestSuccess

[8]ページ先頭

©2009-2025 Movatter.jp