Patrick
Patrick

Reputation: 876

how do I group all the distinct number using ramdajs

Suppose, I have an array which contains value [1,1,2,2,3,3]. I want to group all the distinct number in this array so I should have the result like this { "0": [1,2,3], "1": [1,2,3] }
So here what I have tried. The inital value actually start at 1-99 so if I % by 10 all will have something like [0,..,9,0,...,9] and after that I don't know what to do next.

const test = compose(
    groupBy(i => ...),
    map(i => i % 10),
    range
  )(1, 10*10);

this worked for me

const test = compose(
    mapObjIndexed((value,key,obj) => value.map((numb,index) => obj[index][key])),
    groupBy(i => i),
    map(i => i % 10),
    range
  )(1, 10*10)

Upvotes: 1

Views: 352

Answers (3)

Scott Sauyet
Scott Sauyet

Reputation: 50797

The idea customcommander presents of using transpose makes this pretty easy. Two further steps seem to get you fully to your goal... if I understand that goal correctly. First, we can replace groupWith(equals) with groupBy(identity) followed by values; this means it no longer matters whether the input is sorted. Second, if you really want the {0: [1, 2, 3], 1: [1, 2, 3]} output rather than the more common [[1, 2, 3], [1, 2, 3]], we can end with a toPairs/fromPairs shuffle (equivalently, Object.entries/Object.fromEntries.) On a plain object, that would be a no-op. But on an array, it converts it to an object with such integer keys.

const group = pipe (
  groupBy (identity),
  values,
  transpose,
  toPairs,
  fromPairs
) 

console .log (group ([1, 1, 2, 2, 3, 3]))

console .log (group ([1, 5, 1, 2, 4, 2, 3, 3, 1, 2, 1, 3, 1, 1, 3, 2, 1, 4]))
.as-console-wrapper {min-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {pipe, groupBy, identity, values, transpose, toPairs, fromPairs} = R </script>

So this is my understanding of what you're trying to achieve. Please let me know if this is wrong:

The output is an object with keys being the small non-negative integers (0, 1, 2, ...). Key 0 has the unique values from the original input. Key 1 has the unique values remaining after you remove one of each element of key 0 from the input. Key 2 has the unique values remaining after you remove one of each element of key 0 from the input and then one of each element of key 1 from the remainder, and so forth.

Note that although the results have the elements sorted, that's a happy accident, based on the strange key-sorting algorithm for small integers in JS. If you were to replace the 1's by a's, the 2's by b's and so forth in the second example, the results would be sorted differently:

console .log (group (['a', 'e', 'a', 'b', 'd', 'b', 'c', 'c', 'a', 'b', 'a', 'c', 'a', 'a', 'c', 'b', 'a', 'd']))
//=> {"0":["a","e","b","d","c"],"1":["a","b","d","c"],"2":["a","b","c"],"3":["a","b","c"],"4":["a"],"5":["a"],"6":["a"]}

If this is an issue, you could end the pipeline with map (sortBy (identity)).

Upvotes: 2

customcommander
customcommander

Reputation: 18901

Assuming your list is sorted, it could be as simple as:

This may be an oversimplification of your problem at hand but this solution may be enough to get you started

const group = compose(transpose, groupWith(equals));

console.log(group([1,1,2,2,3,3]));
//=> [[1,2,3],[1,2,3]]
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.min.js"></script>
<script>const {compose, equals, groupWith, transpose} = R;</script>

What's happening here?

First groupWith(equals) creates sequences of similar adjacent numbers (which is why the list must be sorted)

groupWith(equals, [1,1,2,2,3,3]);
//=> [[1,1],[2,2],[3,3]])

Then transpose will create from x lists of n length, n lists of x length:

transpose([[1,1],[2,2],[3,3]])
//=> [[1,2,3],[1,2,3]]

We had 3 lists of 2 items, we now have 2 lists of 3 items.

It should now be easy to produce a map from it if necessary.

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386634

You could take an object which keeps the index for the target of the same value.

This approach fills the first arrays first.

var data = [1, 1, 2, 2, 3, 3],
    indices = {},
    result = {};

for (let value of data) {
    if (!indices[value]) indices[value] = 0;
    const index = indices[value]++;
    if (!result[index]) result[index] = [];
    result[index].push(value);
}

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Related Questions