Giter VIP home page Giter VIP logo

ramda's Introduction

Ramda

A practical functional library for JavaScript programmers.

Build Status npm module deno land nest badge Gitter

Why Ramda?

There are already several excellent libraries with a functional flavor. Typically, they are meant to be general-purpose toolkits, suitable for working in multiple paradigms. Ramda has a more focused goal. We wanted a library designed specifically for a functional programming style, one that makes it easy to create functional pipelines, one that never mutates user data.

What's Different?

The primary distinguishing features of Ramda are:

  • Ramda emphasizes a purer functional style. Immutability and side-effect free functions are at the heart of its design philosophy. This can help you get the job done with simple, elegant code.

  • Ramda functions are automatically curried. This allows you to easily build up new functions from old ones simply by not supplying the final parameters.

  • The parameters to Ramda functions are arranged to make it convenient for currying. The data to be operated on is generally supplied last.

The last two points together make it very easy to build functions as sequences of simpler functions, each of which transforms the data and passes it along to the next. Ramda is designed to support this style of coding.

Introductions

Philosophy

Using Ramda should feel much like just using JavaScript. It is practical, functional JavaScript. We're not introducing lambda expressions in strings, we're not borrowing consed lists, we're not porting over all of the Clojure functions.

Our basic data structures are plain JavaScript objects, and our usual collections are JavaScript arrays. We also keep other native features of JavaScript, such as functions as objects with properties.

Functional programming is in good part about immutable objects and side-effect free functions. While Ramda does not enforce this, it enables such style to be as frictionless as possible.

We aim for an implementation both clean and elegant, but the API is king. We sacrifice a great deal of implementation elegance for even a slightly cleaner API.

Last but not least, Ramda strives for performance. A reliable and quick implementation wins over any notions of functional purity.

Installation

To use with node:

$ npm install ramda

Then in the console:

const R = require('ramda');

To use directly in Deno:

import * as R from "https://deno.land/x/[email protected]/mod.ts";

or using Nest.land:

import * as R from "https://x.nest.land/[email protected]/mod.ts";

To use directly in the browser:

<script src="path/to/yourCopyOf/ramda.js"></script>

or the minified version:

<script src="path/to/yourCopyOf/ramda.min.js"></script>

or from a CDN, either cdnjs:

<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.29.1/ramda.min.js"></script>

or one of the below links from jsDelivr:

<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/ramda.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/ramda@latest/dist/ramda.min.js"></script>

(note that using latest is taking a significant risk that ramda API changes could break your code.)

These script tags add the variable R on the browser's global scope.

Or you can inject ramda into virtually any unsuspecting website using the bookmarklet.

Note for versions > 0.25 Ramda versions > 0.25 don't have a default export. So instead of import R from 'ramda';, one has to use import * as R from 'ramda'; Or better yet, import only the required functions via import { functionName } from 'ramda';

Note for ES6 module and browsers In order to access to the ES6 module in browsers, one has to provide the content of the es directory (see below for the build instructions) and use import * as R from './node_modules/ramda/es/index.js';

Build

npm run build creates es, src directories and updates both dist/ramda.js and dist/ramda.min.js

Partial Builds

It is possible to build Ramda with a subset of the functionality to reduce its file size. Ramda's build system supports this with command line flags. For example if you're using R.compose, R.reduce, and R.filter you can create a partial build with:

npm run --silent partial-build compose reduce filter > dist/ramda.custom.js

This requires having Node/io.js installed and ramda's dependencies installed (just use npm install before running partial build).

Documentation

Please review the API documentation.

Also available is our Cookbook of functions built from Ramda that you may find useful.

The Name

Ok, so we like sheep. That's all. It's a short name, not already taken. It could as easily have been eweda, but then we would be forced to say eweda lamb!, and no one wants that. For non-English speakers, lambs are baby sheep, ewes are female sheep, and rams are male sheep. So perhaps ramda is a grown-up lambda... but probably not.

Running The Test Suite

Console:

To run the test suite from the console, you need to have mocha installed:

npm install -g mocha

Then from the root of the project, you can just call

mocha

Alternately, if you've installed the dependencies, via:

npm install

then you can run the tests (and get detailed output) by running:

npm test

Browser:

You can use testem to test across different browsers (or even headlessly), with livereloading of tests. Install testem (npm install -g testem) and run testem. Open the link provided in your browser and you will see the results in your terminal.

If you have PhantomJS installed, you can run testem -l phantomjs to run the tests completely headlessly.

Usage

For v0.25 and up, import the whole library or pick ES modules directly from the library:

import * as R from 'ramda'

const {identity} = R
R.map(identity, [1, 2, 3])

Destructuring imports from ramda does not necessarily prevent importing the entire library. You can manually cherry-pick methods like the following, which would only grab the parts necessary for identity to work:

import identity from 'ramda/src/identity'

identity()

Manually cherry picking methods is cumbersome, however. Most bundlers like Webpack and Rollup offer tree-shaking as a way to drop unused Ramda code and reduce bundle size, but their performance varies, discussed here. Here is a summary of the optimal setup based on what technology you are using:

  1. Webpack + Babel - use babel-plugin-ramda to automatically cherry pick methods. Discussion here, example here
  2. Webpack only - use UglifyJS plugin for treeshaking along with the ModuleConcatenationPlugin. Discussion here, with an example setup here
  3. Rollup - does a fine job properly treeshaking, no special work needed; example here

Typings

Translations

Funding

If you wish to donate to Ramda please see our Open Collective page. Thank you!

Acknowledgements

Thanks to J. C. Phillipps for the Ramda logo. Ramda logo artwork © 2014 J. C. Phillipps. Licensed Creative Commons CC BY-NC-SA 3.0.

ramda's People

Contributors

adispring avatar andarist avatar anton-rudeshko avatar arcseldon avatar asaf-romano avatar benperez avatar bradcomp avatar buzzdecafe avatar cgarciae avatar crosseye avatar customcommander avatar davidchambers avatar fson avatar gilligan avatar gyeates avatar jaforbes avatar jethrolarson avatar jimf avatar kedashoe avatar ku8ar avatar megawac avatar ndhoule avatar nfantone avatar paldepind avatar raine avatar safareli avatar scott-christopher avatar sebastienfilion avatar theludd avatar wojpawlik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ramda's Issues

Minor Cleanup

Some minor clean-up tasks:

  • remove existing and, or, and not functions, and reassign andFn, orFn, and notFn to these names.
  • add last similar to first/head
  • rewrite join, indexOf, lastIndexOf, etc to use invoker. Alternately: adjust these to handle arguments objects as well as arrays.
  • consider adding a public slice implementation, which would probably mean renaming the internal one. The public one should have the signature slice(start, end, arr) and be autocurried, and possibly have sliceFrom/sliceTo glosses to handle the missing parameters. This would probably have to be different from the private implementation we already have or we'd introduce a circular dependency with curry.

Fix `uniq`

param order is wrong, and should probably not reverse the list. Easy fixes.

url for documentation

The link leading to "annotated source code documentation" in the README.md has a wrong url.
It should be without the %0A ( one of the last characters )

and..
I just want to thank @CrossEye for all the great work and engagement on the functional javascript topics. Thanks!

Create a contribution Wishlist or Roadmap

Hi,

I want to use/help create a good functional programming library, so I've decided to contribute. I've asked @DrBoolean and he told here would be a great place for it, but I don't know where to start. If you create a wishlist or roadmap of features (new functions, documentations, test, bug correction, etc) that you would like to have, it would be easier for new contributors. Items on the document could even be debated, and would help organize the effort.

If such a document exists and you would be kind enough to share it would be of great help. I'll start by using ramda to get familiarized with it.

BTW: I've been working on a little type safety system for functions using Haskell syntax. I don't think anyone would like to type everything, but restriction might be useful sometimes.
https://github.com/cgarciae/TypeJS

strLastIndex

Documentation for strLastIndex should be

        //     strLastIndexOf('a', 'banana split') //=> 2

instead of

        //     strIndexOf('a', 'banana split') //=> 2

ramda.* vs R.*

Ramda is exported as ramda, which is very descriptive but its long, normally libraries create two letter module. How exporting ramda as R as is in the source code so accessing the functions is much easier/faster?

find

add find fn to return first match in a list
find fn -> list -> element

also
experiment with underscore/lodash style POJSO notation for specifying constraints

Small code feedback.

.1. Would be really nice to have some examples, at least very basic, for 'fork' and 'wrap' functions use-cases.

.2. Maybe makes sense to add 'unary', 'binary' service functions to limit arity as a special cases.
Something like this:

unary = function (fn) {
  return function unary (arg) {
    return fn.call(this, arg);
  };
};

possible use-cases:

if (hasMethod('filter', list)) {
  return list.filter(useIdx ? fn : unary(fn)); // TODO: figure out useIdx
}
max = function (list) {
  return list.reduce(binary(Math.max));
}

.3. Btw using Math min/max call like this: Math.max.apply(null, list); have some limitations and works less performant than usual reduce.

.4. Is it possible to unmemoize or make force call disregarding cache with memoized function?

.5. 'always' can have alias 'constant'

.6. Just in case: there are also very promising projects: https://github.com/dtinth/it.js and http://danieltao.com/lazy.js/

Want something like 'nCopiesOf', but obvious signature doesn't really work with currying

I just added streamOf, which creates a lazy list of a single value, and which can be used with take to initialize a list of identical elements:

take(5, streamOf(0)); //=> [0, 0, 0, 0, 0]
take(4, streamOf(null)); //=> [null, null, null, null]

But I would like a single-function gloss on this pattern. It would be useful for my current project, and I think it would be useful overall. I would call it nCopiesOf, but that name seems to almost demand the signature (n, value). Yet the more useful curried version, I'm pretty sure, would be to curry the value, not the count (e.g. var copiesOfZero = flip(nCopiesOf)(0); ... var fiveZeros = copiesOfZero(5);). We clearly don't want the user to have to flip every time before currying with the value.

Is there some equally clear name that would permit a signature of (value, n) instead?

Add path support to R.prop or add a new R.path

Hi, i'm working with the library and i found a case in which i have an array of nested objects. I found myself trying to do this at first:

R.pluck('data.advertiser.value')

Which didn't worked as it's not supported, and then i had to resort to this:

R.compose(R.pluck('value'), R.pluck('advertiser'), R.pluck('data'))

Which looks ugly and discarded in favor of:

R.map(function(item) { return item.data.advertiser.value; }

I have a strong feeling that this could be resolved in two possible ways:

Number one: by making R.prop support paths.

R.pluck('data.advertiser.value')

Number two: by making a new R.path function that supports paths.

R.map(R.path('data.advertiser.value'))
R.pluckPath('data.advertiser.value'))

In both cases there's the possibility of integrating mariocasciaro/object-path into Ramda for easier implementation.

mapObj.idx?

something like:

function mapObj.idx(fn, obj) {
       return foldl(function (acc, key) {
           acc[key] = fn(obj[key], key, obj);
           return acc;
       }, {}, keys(obj));
}

(curried of course)

thoughts? any reason why not? the reason I ask, is because our one dependent package needs this ...

R.cloneDeep is more like "make JSON-safe"

The comment reads // Create a deep copy of an object. but the implementation is JSON.parse(JSON.stringify(obj)). This means some values, most notably functions, won't be included in the returned value.

var obj = {fn:function(){}, undef: undefined, regex: /ab+c/i}
JSON.parse(JSON.stringify(obj))
// { regex: {} }

There are many choices, including:

  1. change the function name
  2. change the behavior to use the structured cloning algorithm
  3. change the behavior to allow all values (including functions, RegEx's, etc)

I'd prefer #'s 2 or 3, but I think it's most important that the name and behavior agree.

Browser support

I noticed a few cases where some functions won't work in old ie (specifically indexOf). Is this library IE>=9 (it would be about an extra 8 loc for IE>7)

R.map doesn't support Sparse Arrays like the standard map

I think it would be best to have another function, e.g. R.map.sparse, that does the standard map behavior.

Here is some code that will demonstrate the issue:

var R = require('ramda');
var sparse = [, 2, 3];
var mapResults = sparse.map(function(item) {
    return item;
});
console.log(mapResults);
var RmapResults = R.map(function(item) {
    return item;
}, sparse);
console.log(RmapResults);

foldl1 throws

At present:

 R.foldl1 = _(function (fn, list) {
        if (isEmpty(list)) {
            throw new Error("foldl1 does not work on empty lists");
        }
        return foldl(fn, head(list), tail(list));
    });

Why not:

R.foldl1 = _(function (fn, list) {
        if (isEmpty(list)) {
            return [];
        }
        return foldl(fn, head(list), tail(list));
    });

?

Implementation of Maybe

Currently Maybe is a class that has some dependent behavior, but if Maybe is decoupled into Just and Nothing, like in Haskell, each is simpler and more performant.

Take this map example:

Just.prototype.map = function (f) {
    return new Just (f (this.value));
};

Nothing.prototype.map = function (f) {
    return new Nothing(); //or return this?
};

As you see, each subclass already knows what to do, no need to check if isNil all the time.

Also, at least in Haskell, you can't have an instance of Maybe something, Maybe is more like an interface or an abstract class. But if you wan't to keep the constructor for compatibility, Maybe should just be the function:

function Maybe (x) {
    return isNil(x) ? new Nothing() : new Just (x);
}

Consider changing `minWith` and `maxWith` APIs to be more like `sortBy`

sortBy has a nicer API than do minWith and maxWith.

In sortBy we generate a key for the objects to sort, and the comparator to use is automatically created based on this key. So we can do something as simple as

sortBy(prop('title'), list);

minWith and maxWith, by contrast, which don't even need to be sorted, require you to supply a comparator.

minWith(function(a, b) {return a.x - b.x;}, points)

This really should be

minWith(prop('x'), points);

Granted the current API offers some theoretic additional flexibility. But I can't see any realistic cases that would take advantage of it.

If there are no objections, I'll do this over the weekend.

Curried Binary Compose

Most curried binary operators can be lifted to then be applied to two consecutive functors. Example

Maybe.of( R.add ).ap( Maybe (2) ).ap( Maybe (3) ); // Just 5

You should have the possibility to do this with compose. For that you need a curried binary compose (compose2). Example

Maybe.of( R.compose2 ).ap( Maybe (R.add(2)) ).ap( Maybe (R.add(3)) ); // Just (+5)

npm package

Thanks for writing Ramda—it's really scratching an itch I've had for a while. Would it be possible for you to put it up on npm? I'd love to use it in Node.

Reimplementation of Compose 24x Faster!

I've done an implementation of compose, it fairly straight forward, but I've done some tests and it proves to be far more efficient than the current implementation of Ramda. Here I am actually going show 2 variants of my implementation.

Both depend on this functions

function compose2(f,g){
    return function(x){
        return f(g(x));
    };
}

which just does composition by pairs. Based on this two compose methods are proposed: the first one uses a forloop which accumulates the composition as it goes along the array of functions.

function composeV1() {
    var i,
        f = arguments[0];
    for (i = 1; i < arguments.length; i++) {
        f = compose2(f,arguments[i]);
    }
    return f;
}

The other method does the same but using reduce

function composeV2() {
    return Array.prototype.reduce.call(arguments, function(f,g){
        return compose2(f,g);
    });
}

which is more functional but, as we'll see, a lot slower. Here is a test comparing both the construction of the composite function as the execution of it.

var i, n = 1000000;

console.time ('ramdaComposition');
for (i = 0; i < n; i++) {
    fR = ramda.compose(add1, square, double, add1, square, double);
}
console.timeEnd ('ramdaComposition');

console.time ('v1Composition');
for (i = 0; i < n; i++) {
    fV1 = composeV1(add1, square, double, add1, square, double);
}
console.timeEnd ('v1Composition');

console.time ('v2Composition');
for (i = 0; i < n; i++) {
    fV2 = composeV2(add1, square, double, add1, square, double);
}
console.timeEnd ('v2Composition');

console.time ('fR');
for (i = 0; i < n; i++) {
    fR(5);
}
console.timeEnd ('fR');

console.time ('fV1');
for (i = 0; i < n; i++) {
    fV1(5);
}
console.timeEnd ('fV1');

console.time ('fV2');
for (i = 0; i < n; i++) {
    fV2(5);
}
console.timeEnd ('fV2');

The first three construct the composite functions fR, fV1 and fV2, and the last three just execute them with an input value. Each cycle last 1000000 iterations. The results are

ramdaComposition: 154ms
v1Composition: 107ms
v2Composition: 501ms

fR: 1356ms
fV1: 55ms
fV2: 56ms

In the composition phase, comparison to ramda.compose
composeV1 : 1.439X
composeV2 : 0,307X

So composeV1 is actually faster, but composeV2 is incredibly slow. This is due to the conversion of arguments to Array.

The results for execution are far more impressive in comparison to fR:
fV1 : 24.65X
fV2 : 24.21X

This is a HUGE improvement and the implementation is far more readable than the current one. So I propose the use of composeV1 as the standard implementation.

The dummy functions used here are:

function add1(a){return a+1;}
function square(a){return a*a;}
function double(a){return 2*a;}

Examine API of `splice`

This does not match the native splice API, as all it does is remove part of the list without allowing you to add something new.

We should look to see if this is what we really want.

splice is hard because of all the optional parameters to the native function...

Don't curry internal functions

As mentioned in #72, an internal reference to any function to be used internally should be kept (non auto curried) for performance. At the end of ramda.js expose all functions

R.all = _(all);
R.any = _(any);
R.foo = _(foo);

On another note, I disagree with separating map and map.idx I think the idx functions should be used by default. I think a more suitable approach would be to introduce an argLimiter function to the api for partially application via a map/filter

time to make `each` public

working with the DOM has convinced me of the need for an each method on ramda.
The DOM does not lend itself to immutability. Or referential transparency. Or elegance. so this is one vote for exposing a bit of impurity (gasp) in the lib for the sake of practicality.

Note that since we are not using native array methods (e.g. slice) internally, we can use ramda seamlessly with NodeLists et al.

Thoughts?

Long-term: consider expanding API our that accepts arrays to also accept generators

In examples/generators.js there is a simple version of generators code, using a function that creates a head/tail generator out of

  • a seed
  • a function that calculates the current value from the seed
  • and a function that calculates the next seed.

This style generator can be used as the basis for lazy lists, and there is already sample code included to run maps and filters over them, as well as, of course, take and skip.

The question is whether it's worth expanding our API for map and filter and so on to handle these. It would complicate implementation quite a bit, and we'd have to do some nasty type-checking. It's not clear if there's any great reason for this, except that a unified API for finite and infinite lists would be extremely convenient.

I would not consider doing this right away. We need to play around with the array-based implementation quite a bit first. But it might make for a very interesting library if we were to manage this.

Of the use of hasMethod() in the implementations of many functions

Why call a method with the same name if it exists ? I fear that it may lead to some incomprehensions if a method with the same name as one we want to use exists on the object but does something completely different - and when I use R.take, for instance, it is the function that I want called.

Why not use something like R.method('name', args...) instead to leave the choice up to the user of the library ?

var obj = [1, 2, 3];
obj.take = function() { return this[1];  } //

R.take(2, obj); // will return 2, but I wanted [1, 2]

// I propose
R.method("take", obj); // 2
R.take(2, obj); // [1, 2]

I'm just worried that since javascript is so permissive, people will end up applying ramda's function on objects they have completely modified beforehand and get unexpected results.

iterator spec

Here is the Iterator API I am considering. Any comments, or shall i just go ahead and implement it?

describe('iterator', function() {                                                                                                                     
    var iterator = Lib.iterator;                                                                                                                      
    var I = Lib.I;                                                                                                                                    
    var arr = [1,2,3,4];                                                                                                                              
    it("returns an iterator object", function() {                                                                                                     
      var iter = iterator(I, arr);                                                                                                                    
      assert.equal(typeof iter, "object");                                                                                                            
      assert.equal(typeof iter.hasNext, "function");                                                                                                  
      assert.equal(typeof iter.next, "function");                                                                                                     
      assert.equal(iter["\u03BB-iter"], true);                                                                                                        
    });                                                                                                                                               
    it("applies the transform function to each element as it returns it", function() {                                                                
      var square = function(x) { return x * x; };                                                                                                     
      var sqIter = iterator(square, [5,7,9]);                                                                                                         
      assert.equal(sqIter.next(), 25);                                                                                                                
      assert.equal(sqIter.next(), 49);                                                                                                                
      assert.equal(sqIter.next(), 81);                                                                                                                
    });                                                                                                                                               
    describe('next', function() {                                                                                                                     
      it("returns the next element in the list, duh", function() {                                                                                    
        var iter = iterator(I, [100, 1000, 10000]);                                                                                                    
        assert.equal(iter.next(), 100);                                                                                                               
        assert.equal(iter.next(), 10000;                                                                                                              
        assert.equal(iter.next(), 10000);                                                                                                             
        assert.equal(iter.next(), 10000);                                                                                                             
      });                                                                                                                                             
    });                                                                                                                                               
    describe('hasNext', function() {                                                                                                                  
      it("returns true when there is another element to iterate, else false", function() {                                                            
        var iter = iterator(I, [2]);                                                                                                                     
        assert.equal(iter.hasNext(), true);                                                                                                           
        assert.equal(iter.next(), 2);                                                                                                                 
        assert.equal(iter.hasNext(), false);                                                                                                          
      });                                                                                                                                             
    });                                                                                                                                               
});                                 

build/bundle process

we need a way to componentize the lib so users may build a version as slim or full as they need. There's no reason for us to inflict algebraic types on everybody, 'cuz most folks probably can do without.

Candidates for modularization:

definitely not in core:
Lazylists
Types (Maybe, Either, IO, EventStream)

possibly pulled out into extensions:
math fns
sql-y query functions
more?

Have to see how other projects handle this problem (e.g. lodash? http://lodash.com/custom-builds)

zip should use the length of the largest array

Most other libraries use a zip implementation that will apply the length of the largest array for the result.

zip([1, 2, 3], ['a', 'b', 'c', 'd']); // => [[1,"a"],[2,"b"],[3,"c"],[undefined,"d"]]

Is this like RxJS, or could it be extended to handle time-based sequences?

Ramda looks really cool, and clean. RxJS includes the capabilities of Ramda, but extends the concept to handle time-based sequences (user interactions that occur over time is one example). Any thoughts about how RxJS (which is a mess in terms of the code and documentation) could be replaced with Ramda?

Usefulness of containsWith

Is containWith that useful to have as an API function ?

I may be wrong, but I'd rather do something like

R.contains(R.lPartial(predicate, value), list) // could also be curry, why not.

which imo conveys better what really happen.

Is there a real utility for the .idx functions ?

Is the performance gain that impactful when calling a function with only one element instead of three ?

I personally think that map(...) providing (value, index, list) to the callback in any case is not that counter-intuitive since nothing prevents me to give a callback that only names the first one - after all, doesn't jquery, underscore and the likes already do so ?

To have a .idx() feels superfluous, and can lead the way to have "forgotten" functions like all() who doesn't have an .idx() version and yet who could have one.

Thoughts ?

Add `groupBy`, `countBy`, and `sortBy` functions

groupBy, countBy, and sortBy are generally useful functions:

var albums = [
    {title: 'Art of the Fugue', artist: 'Glenn Gould', genre: 'Baroque'},
    {title: 'A Farewell to Kings', artist: 'Rush', genre: 'Rock'},
    {title: 'Timeout', artist: 'Dave Brubeck Quartet', genre: 'Jazz'},
    {title: 'Fly By Night', artist: 'Rush', genre: 'Rock'},
    {title: 'Goldberg Variations', artist: 'Daniel Barenboim', genre: 'Baroque'},
    {title: 'New World Symphony', artist: 'Leonard Bernstein', genre: 'Romantic'},
    {title: 'Romance with the Unseen', artist: 'Don Byron', genre: 'Jazz'},
    {title: 'Somewhere In Time', artist: 'Iron Maiden', genre: 'Metal'},
    {title: 'In Times of Desparation', artist: 'Danny Holt', genre: 'Modern'},
    {title: 'Evita', artist: 'Various', genre: 'Broadway'},
    {title: 'Five Leaves Left', artist: 'Nick Drake', genre: 'Folk'},
    {title: 'The Magic Flute', artist: 'John Eliot Gardiner', genre: 'Classical'}
];

sortBy(prop("title"), albums); //=> sorted list

countBy(prop("artist"), albums); //=> object mapping artist to number

groupBy((function() {
    var remap = {
        Baroque: 'Classical',
        Modern: 'Classical',
        Romantic: 'Classical',
        Metal: 'Rock'  /*, etc */
    }
    return function(album) {
        var genre = prop("genre", album);
        return remap[genre] || genre;
    };
}()), albums); //=> object mapping super-genre to list of albums

These are meant to be sort, count, and group by (possibly derived) keys, not arbitrary comparators. We might still want a more general sort, or more likely a comparator builder based on a predicate. This is just a single key based on only one object.

PROPOSAL [.then]

SIGNATURE
then :: (a -> b) -> (b -> c) -> (a -> c)
Note: then is actually meant to be added to Function.prototype

IDEA
Using the same Promise syntax, with then you can "compose" or sequence functions in a way that gives a good idea what is being computed.

INSPIRATION
http://scott.sauyet.com/Javascript/Talk/2014/01/FuncProgTalk/#slide-124

EXAMPLE

var FoG = G.then (F);
var login =
    startProgram
        .then (goToMainWindow, clickOnLogin)
        .then (typeUserName, typePassword)
        .then (clickOk);

IMPLEMENTATION

Function.prototype.then = function () {
    return R.pipe.apply (this, [this].concat (argsToArray (arguments)));
}

OPTIONAL
This introduces a new argsToArray function, that converts arguments to array.

var argsToArray = R.argsToArray = function (_args) {
  var args = new Array(_args.length),
      i = -1;
  while (++i < _args.length) {
    args[i] = _args[i];
  }
  return args;
};

This implementation of argsToArray is an adaptation of f5 in this test http://jsperf.com/arguments-to-array/5, which has the best marks, and could be used inside many other functions, including compose.

`reduce` should be an alias of `foldl1`, not `foldl`.

Generally when I've seen languages differentiate reduce and fold, it's by whether or not the zero parameter (as in foldl(fn, zero, list)) is assumed from the first list element.

Stack Overflow corroborating my thoughts:
http://stackoverflow.com/a/9055893

Counterpoint:
JavaScript's native reduce allows specifying the zero parameter.

Counter-counterpoint:
JavaScript doesn't have a fold to contrast with reduce, and Ramda seems to prefer separate functions over variadic ones.

Old IE fixes

Fixing the tests (see #126) to run in older IE brought out that these tests are failing at least in IE9: sliceFrom, strIndexOf and strLastIndexOf.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.