Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

_.groupsOf #696

Closed
wants to merge 1 commit into from
Closed

_.groupsOf #696

wants to merge 1 commit into from

Conversation

198d
Copy link

@198d 198d commented Aug 10, 2012

Needed something like this earlier today and was almost sure I would have found it in underscore. Super surprised when I didn't. Take it or leave it.

Includes tests and update to index.html for documentation. Ran rake doc, but changes produced seemed too big to be from just me, didn't spend much time looking at the diff though; backed them out, regardless.

@spadgos
Copy link
Contributor

spadgos commented Aug 10, 2012

I've seen this implemented in other languages under the name "chunk". Not that PHP is a bastion of good naming, but for example: array_chunk. groupsOf made me think it would group data by some criteria.

@Mithgol
Copy link

Mithgol commented Aug 10, 2012

And “chunk” is shorter than “groupsOf”.

@michaelficarra
Copy link
Collaborator

I think it would be better to introduce an unfoldr so people can easily build these functions themselves.

> let groupsOf n = unfoldr $ \x -> if null x then Nothing else Just $ splitAt n x
> groupsOf 3 [0..7]
[[0,1,2],[3,4,5],[6,7]]
> 

LiveScript's Prelude.ls has already implemented it. Take a look at the documentation and the JS source.

edit: point-free style

Add function that takes an array and a chunk size n and produces an
array of arrays of size n. Add tests and update index.html to cover new
function.
@noprompt
Copy link

I'm with @michaelficarra on this. unfoldr would supply a general solution.

@Mithgol
Copy link

Mithgol commented Aug 23, 2012

Thank you for accepting the suggested _.chunk() name, @198d.

@michaelficarra
Copy link
Collaborator

unfoldr is also useful in the definition of sliding from the proposal in #714

> let sliding n list = if n > length list then [list] else unfoldr (\x -> if n > length x then Nothing else Just (take n x, drop 1 x)) list
> sliding 3 [1..8]
[[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8]]
>

@jdalton
Copy link
Contributor

jdalton commented Aug 23, 2012

@michaelficarra could you translate that to JS for the non-CS readers plz.

@michaelficarra
Copy link
Collaborator

@jdalton: It's Haskell, actually.

function groupsOf(n, list) {
  return _.unfoldr(function(x){ return x.length === 0 ? null : [x.slice(0, n), x.slice(n)]; }, list);
};
function sliding(n, list){
  if(n > list.length) return [list];
  return _.unfoldr(function(x){ return n > x.length ? null : [x.slice(0, n), x.slice(1)]; }, list);
};

@jdalton
Copy link
Contributor

jdalton commented Aug 23, 2012

: It's Haskell, actually.

Oh whew thanks, heh that explains why I got errors attempting to convert it from CS to JS via the coffeescript.org demo :P

@noprompt
Copy link

It's a shame JavaScript doesn't have the concept of tuples. The caveat to unfoldr is the function passed to it must return either null (Nothing) or a pair [x1, x2] (Just (a, b)). This might trip some people up since it's not the typical way people think about JavaScript code. That aside, I still think unfoldr would make an awesome addition to underscore - there are two open issues it would close and possibly more in the future.

Perhaps adding unfoldr should be separate issue. Maybe we could ask @gkz if it would be ok to port his unfoldr solution from prelude to underscore? Or maybe I'm jumping the gun here.

@gkz
Copy link

gkz commented Aug 25, 2012

It's cool with me. I just ported it from Haskell - you'll probably be able to make a more efficient version.

@noprompt
Copy link

The implementation in this gist is more efficient since it doesn't rely on recursion.

@jashkenas
Copy link
Owner

I'm not such a big fan of adding unfoldr, and @michaelficarra's last comment shows a great demonstration why -- the implementations of groupsOf and sliding in terms of unfoldr are less readable than the vanilla versions.

As for "groupsOf" or "chunk" ... got any good use cases that don't involve starting with a strange data source that passes what should be tuples as a single list?

@knowtheory
Copy link
Collaborator

@jashkenas don't many client-side paginators depend on logic similar to chunk?

@jashkenas
Copy link
Owner

I don't think so ... even if you're showing a list of page numbers, it's often starting from an arbitrary point, and you only need a single "chunk" of numbers, not all of them.

For example, when you're on page 7 of google results, you see 2 through 11, with 7 highlighted. Doing an _.chunk(1, lordKnowsHowManyGazillionPageOfGoogleResults) wouldn't be helpful at all.

@knowtheory
Copy link
Collaborator

Yeah, checking on backbone.paginator, it uses calculated pages, rather than prepared chunks.

@noprompt
Copy link

Agreed, they are less readable. The issue with groupsOf and sliding is they are special cases of a general solution. If these functions are added to the library, provided there are valid use cases, then it might make sense to add an implementation of unfoldr.

The downsides with unfoldr are it's mild learning curve and the special requirements of the passed function. While functions implemented with it could appear less readable most of that responsibility belongs to the programmer.

@Sephi-Chan
Copy link

Sorry for my duplicate #758. For now I use this implementation : https://gist.github.com/3636726.

I would be glad to have it in Underscore since I often use that for display purposes.
I understand though that it's not useful everyday.

@jashkenas
Copy link
Owner

I think we should leave this as an _.mixin idea for now ... absent more/better use cases.

@jashkenas jashkenas closed this Sep 18, 2012
@LarryBattle
Copy link

For those still craving for this functionality, here's the code for _.chunk().

_.mixin({
    chunk : function (array, unit) {
        if (!_.isArray(array)) return array;
        unit = Math.abs(unit);
        var results = [],
        length = Math.ceil(array.length / unit);

        for (var i = 0; i < length; i++) {
            results.push(array.slice( i * unit, (i + 1) * unit));
        }
        return results;
    }
});

Example:

_.chunk([2,3,4,5], 2); // [[2,3],[4,5]]

Oddly enough this was included in the async branch, but I'm not sure what happened.
61c5f84

@fogus
Copy link
Contributor

fogus commented May 3, 2013

As a point of reference, underscore-contrib related function sin underscore.array.builders.js as partition and partitionAll.

@megawac megawac mentioned this pull request Jul 28, 2015
@jgonggrijp jgonggrijp added the contrib probably belongs in Underscore-contrib label Aug 29, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contrib probably belongs in Underscore-contrib enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.