admin管理员组文章数量:1026989
I have an array of arrays and want to write a function that returns the top x
number of items, by taking items from each array in order.
Here is an example of what I'm after:
const input = [
["1a", "2a", "3a", "4a", "5a"],
["1b", "2b", "3b", "4b", "5b"],
["1c", "2c", "3c", "4c", "5c"],
["1d", "2d", "3d", "4d", "5d"]
];
const takeRoundRobin = count => arr => {
// implementation here
};
const actual = takeRoundRobin(5)(input);
const expected = [
"1a", "1b", "1c", "1d", "2a"
];
I saw a suggestion to a Scala question that solved this using zip
but in Ramda you can only pass 2 lists to zip.
I have an array of arrays and want to write a function that returns the top x
number of items, by taking items from each array in order.
Here is an example of what I'm after:
const input = [
["1a", "2a", "3a", "4a", "5a"],
["1b", "2b", "3b", "4b", "5b"],
["1c", "2c", "3c", "4c", "5c"],
["1d", "2d", "3d", "4d", "5d"]
];
const takeRoundRobin = count => arr => {
// implementation here
};
const actual = takeRoundRobin(5)(input);
const expected = [
"1a", "1b", "1c", "1d", "2a"
];
I saw a suggestion to a Scala question that solved this using zip
but in Ramda you can only pass 2 lists to zip.
4 Answers
Reset to default 6Here, Ramda's transpose
can be your base. Add a dollop of unnest
, a dash of take
, and you get this:
const {take, unnest, transpose} = R
const takeRoundRobin = (n) => (vals) => take(n, unnest(transpose(vals)))
const input = [
['1a', '2a', '3a', '4a', '5a'],
['1b', '2b', '3b', '4b', '5b'],
['1c', '2c', '3c', '4c', '5c'],
['1d', '2d', '3d', '4d', '5d']
]
console.log(takeRoundRobin(5)(input))
<script src="//cdnjs.cloudflare./ajax/libs/ramda/0.25.0/ramda.js"></script>
Note also that this can handle arrays of varying lengths:
If you want to be able to wrap around to the beginning and continue taking values, you could replace take
with a recursiveTake
like this:
const {take, unnest, transpose, concat } = R
//recursive take
const recursiveTake = (n) => (vals) => {
const recur = (n,vals,result) =>
(n<=0)
? result
: recur(n-vals.length,vals,result.concat(take(n,vals)))
return recur(n,vals,[]);
};
const takeRoundRobin = (n) => (vals) =>
recursiveTake(n)(unnest(transpose(vals)));
const input = [
['1a', '2a', '3a', '4a'],
['1b'],
['1c', '2c', '3c', '4c', '5c'],
['1d', '2d']
]
console.log(takeRoundRobin(14)(input))
<script src="//cdnjs.cloudflare./ajax/libs/ramda/0.25.0/ramda.js"></script>
Another version of that function, without the explicit recursion would look like:
const takeCyclic = (n) => (vals) => take(
n,
unnest(times(always(vals), Math.ceil(n / (vals.length || 1))))
)
Here's one way you can do it using recursion –
const None =
Symbol ()
const roundRobin = ([ a = None, ...rest ]) =>
// base: no `a`
a === None
? []
// inductive: some `a`
: isEmpty (a)
? roundRobin (rest)
// inductive: some non-empty `a`
: [ head (a), ...roundRobin ([ ...rest, tail (a) ]) ]
It works in a variety of cases –
const data =
[ [ 1 , 4 , 7 , 9 ]
, [ 2 , 5 ]
, [ 3 , 6 , 8 , 10 , 11 , 12 ]
]
console.log (roundRobin (data))
// => [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ]
console.log (roundRobin ([ [ 1 , 2 , 3 ] ]))
// => [ 1 , 2 , 3 ]
console.log (roundRobin ([]))
// => []
Free variables are defined using prefix notation which is more familiar with functional style –
const isEmpty = xs =>
xs.length === 0
const head = xs =>
xs [0]
const tail = xs =>
xs .slice (1)
Verify it works in your browser below –
const None =
Symbol ()
const roundRobin = ([ a = None, ...rest ]) =>
a === None
? []
: isEmpty (a)
? roundRobin (rest)
: [ head (a), ...roundRobin ([ ...rest, tail (a) ]) ]
const isEmpty = xs =>
xs.length === 0
const head = xs =>
xs [0]
const tail = xs =>
xs .slice (1)
const data =
[ [ 1 , 4 , 7 , 9 ]
, [ 2 , 5 ]
, [ 3 , 6 , 8 , 10 , 11 , 12 ]
]
console.log (roundRobin (data))
// => [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ]
console.log (roundRobin ([ [ 1 , 2 , 3 ] ]))
// => [ 1 , 2 , 3 ]
console.log (roundRobin ([]))
// => []
Here's another way using a secondary parameter with default assignment –
const roundRobin = ([ a = None, ...rest ], acc = []) =>
// no `a`
a === None
? acc
// some `a`
: isEmpty (a)
? roundRobin (rest, acc)
// some non-empty `a`
: roundRobin
( append (rest, tail (a))
, append (acc, head (a))
)
const append = (xs, x) =>
xs .concat ([ x ])
To demonstrate what you may have seen as the implementation in other languages, the applicative instance for a ZipList
can be used to transpose the array, where a ZipList
applies the functions contained in the ZipList
in a pair-wise manner with the corresponding ZipList
of values unlike the standard permutative version of ap
for lists.
const ZipList = xs => ({
getZipList: xs,
map: f => ZipList(R.map(f, xs)),
ap: other => ZipList(R.zipWith(R.applyTo, other.getZipList, xs))
})
ZipList.of = x => ZipList(new Proxy([], {
get: (target, prop) =>
prop == 'length' ? Infinity : /\d+/.test(prop) ? x : target[prop]
}))
This has an interesting requirement which is somewhat clunky to represent in JS, where the of
function to produce a "pure" value needs to produce a ZipList
containing a repeating list of the "pure" value, implemented here using a Proxy
instance of an array.
The transpose can then be formed via:
xs => R.unnest(R.traverse(ZipList.of, ZipList, xs).getZipList)
After all of this, we have just reinvented R.transpose
as per the answer from @scott-sauyet.
It is nevertheless an interesting implementation to be aware of.
(full example below)
const ZipList = xs => ({
getZipList: xs,
map: f => ZipList(R.map(f, xs)),
ap: other => ZipList(R.zipWith(R.applyTo, other.getZipList, xs))
})
ZipList.of = x => ZipList(new Proxy([], {
get: (target, prop) =>
prop == 'length' ? Infinity : /\d+/.test(prop) ? x : target[prop]
}))
const fn = xs => R.unnest(R.traverse(ZipList.of, ZipList, xs).getZipList)
const input = [
["1a", "2a", "3a", "4a", "5a"],
["1b", "2b", "3b", "4b", "5b"],
["1c", "2c", "3c", "4c", "5c"],
["1d", "2d", "3d", "4d", "5d"]
];
const expected = [
"1a", "1b", "1c", "1d", "2a"
];
const actual = R.take(5, fn(input))
console.log(R.equals(expected, actual))
<script src="//cdnjs.cloudflare./ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Not sure what Ramda functions to use to address this particular problem but here is an answer not using Ramda that'll only work if all arrays are the same length:
const input = [
['1a', '2a', '3a', '4a', '5a'],
['1b', '2b', '3b', '4b', '5b'],
['1c', '2c', '3c', '4c', '5c'],
['1d', '2d', '3d', '4d', '5d'],
];
const takeRoundRobin = (count) => (arr) => {
const recur = (arr, current, count, result) =>
(current === count)
? result
: recur(
arr,
current + 1,
count,
result.concat(
arr
[current % arr.length]//x value
[//y value
Math.floor(current / arr.length) %
(arr.length + 1)
],
),
);
return recur(arr, 0, count, []);
};
console.log(takeRoundRobin(22)(input));
I have an array of arrays and want to write a function that returns the top x
number of items, by taking items from each array in order.
Here is an example of what I'm after:
const input = [
["1a", "2a", "3a", "4a", "5a"],
["1b", "2b", "3b", "4b", "5b"],
["1c", "2c", "3c", "4c", "5c"],
["1d", "2d", "3d", "4d", "5d"]
];
const takeRoundRobin = count => arr => {
// implementation here
};
const actual = takeRoundRobin(5)(input);
const expected = [
"1a", "1b", "1c", "1d", "2a"
];
I saw a suggestion to a Scala question that solved this using zip
but in Ramda you can only pass 2 lists to zip.
I have an array of arrays and want to write a function that returns the top x
number of items, by taking items from each array in order.
Here is an example of what I'm after:
const input = [
["1a", "2a", "3a", "4a", "5a"],
["1b", "2b", "3b", "4b", "5b"],
["1c", "2c", "3c", "4c", "5c"],
["1d", "2d", "3d", "4d", "5d"]
];
const takeRoundRobin = count => arr => {
// implementation here
};
const actual = takeRoundRobin(5)(input);
const expected = [
"1a", "1b", "1c", "1d", "2a"
];
I saw a suggestion to a Scala question that solved this using zip
but in Ramda you can only pass 2 lists to zip.
4 Answers
Reset to default 6Here, Ramda's transpose
can be your base. Add a dollop of unnest
, a dash of take
, and you get this:
const {take, unnest, transpose} = R
const takeRoundRobin = (n) => (vals) => take(n, unnest(transpose(vals)))
const input = [
['1a', '2a', '3a', '4a', '5a'],
['1b', '2b', '3b', '4b', '5b'],
['1c', '2c', '3c', '4c', '5c'],
['1d', '2d', '3d', '4d', '5d']
]
console.log(takeRoundRobin(5)(input))
<script src="//cdnjs.cloudflare./ajax/libs/ramda/0.25.0/ramda.js"></script>
Note also that this can handle arrays of varying lengths:
If you want to be able to wrap around to the beginning and continue taking values, you could replace take
with a recursiveTake
like this:
const {take, unnest, transpose, concat } = R
//recursive take
const recursiveTake = (n) => (vals) => {
const recur = (n,vals,result) =>
(n<=0)
? result
: recur(n-vals.length,vals,result.concat(take(n,vals)))
return recur(n,vals,[]);
};
const takeRoundRobin = (n) => (vals) =>
recursiveTake(n)(unnest(transpose(vals)));
const input = [
['1a', '2a', '3a', '4a'],
['1b'],
['1c', '2c', '3c', '4c', '5c'],
['1d', '2d']
]
console.log(takeRoundRobin(14)(input))
<script src="//cdnjs.cloudflare./ajax/libs/ramda/0.25.0/ramda.js"></script>
Another version of that function, without the explicit recursion would look like:
const takeCyclic = (n) => (vals) => take(
n,
unnest(times(always(vals), Math.ceil(n / (vals.length || 1))))
)
Here's one way you can do it using recursion –
const None =
Symbol ()
const roundRobin = ([ a = None, ...rest ]) =>
// base: no `a`
a === None
? []
// inductive: some `a`
: isEmpty (a)
? roundRobin (rest)
// inductive: some non-empty `a`
: [ head (a), ...roundRobin ([ ...rest, tail (a) ]) ]
It works in a variety of cases –
const data =
[ [ 1 , 4 , 7 , 9 ]
, [ 2 , 5 ]
, [ 3 , 6 , 8 , 10 , 11 , 12 ]
]
console.log (roundRobin (data))
// => [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ]
console.log (roundRobin ([ [ 1 , 2 , 3 ] ]))
// => [ 1 , 2 , 3 ]
console.log (roundRobin ([]))
// => []
Free variables are defined using prefix notation which is more familiar with functional style –
const isEmpty = xs =>
xs.length === 0
const head = xs =>
xs [0]
const tail = xs =>
xs .slice (1)
Verify it works in your browser below –
const None =
Symbol ()
const roundRobin = ([ a = None, ...rest ]) =>
a === None
? []
: isEmpty (a)
? roundRobin (rest)
: [ head (a), ...roundRobin ([ ...rest, tail (a) ]) ]
const isEmpty = xs =>
xs.length === 0
const head = xs =>
xs [0]
const tail = xs =>
xs .slice (1)
const data =
[ [ 1 , 4 , 7 , 9 ]
, [ 2 , 5 ]
, [ 3 , 6 , 8 , 10 , 11 , 12 ]
]
console.log (roundRobin (data))
// => [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ]
console.log (roundRobin ([ [ 1 , 2 , 3 ] ]))
// => [ 1 , 2 , 3 ]
console.log (roundRobin ([]))
// => []
Here's another way using a secondary parameter with default assignment –
const roundRobin = ([ a = None, ...rest ], acc = []) =>
// no `a`
a === None
? acc
// some `a`
: isEmpty (a)
? roundRobin (rest, acc)
// some non-empty `a`
: roundRobin
( append (rest, tail (a))
, append (acc, head (a))
)
const append = (xs, x) =>
xs .concat ([ x ])
To demonstrate what you may have seen as the implementation in other languages, the applicative instance for a ZipList
can be used to transpose the array, where a ZipList
applies the functions contained in the ZipList
in a pair-wise manner with the corresponding ZipList
of values unlike the standard permutative version of ap
for lists.
const ZipList = xs => ({
getZipList: xs,
map: f => ZipList(R.map(f, xs)),
ap: other => ZipList(R.zipWith(R.applyTo, other.getZipList, xs))
})
ZipList.of = x => ZipList(new Proxy([], {
get: (target, prop) =>
prop == 'length' ? Infinity : /\d+/.test(prop) ? x : target[prop]
}))
This has an interesting requirement which is somewhat clunky to represent in JS, where the of
function to produce a "pure" value needs to produce a ZipList
containing a repeating list of the "pure" value, implemented here using a Proxy
instance of an array.
The transpose can then be formed via:
xs => R.unnest(R.traverse(ZipList.of, ZipList, xs).getZipList)
After all of this, we have just reinvented R.transpose
as per the answer from @scott-sauyet.
It is nevertheless an interesting implementation to be aware of.
(full example below)
const ZipList = xs => ({
getZipList: xs,
map: f => ZipList(R.map(f, xs)),
ap: other => ZipList(R.zipWith(R.applyTo, other.getZipList, xs))
})
ZipList.of = x => ZipList(new Proxy([], {
get: (target, prop) =>
prop == 'length' ? Infinity : /\d+/.test(prop) ? x : target[prop]
}))
const fn = xs => R.unnest(R.traverse(ZipList.of, ZipList, xs).getZipList)
const input = [
["1a", "2a", "3a", "4a", "5a"],
["1b", "2b", "3b", "4b", "5b"],
["1c", "2c", "3c", "4c", "5c"],
["1d", "2d", "3d", "4d", "5d"]
];
const expected = [
"1a", "1b", "1c", "1d", "2a"
];
const actual = R.take(5, fn(input))
console.log(R.equals(expected, actual))
<script src="//cdnjs.cloudflare./ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Not sure what Ramda functions to use to address this particular problem but here is an answer not using Ramda that'll only work if all arrays are the same length:
const input = [
['1a', '2a', '3a', '4a', '5a'],
['1b', '2b', '3b', '4b', '5b'],
['1c', '2c', '3c', '4c', '5c'],
['1d', '2d', '3d', '4d', '5d'],
];
const takeRoundRobin = (count) => (arr) => {
const recur = (arr, current, count, result) =>
(current === count)
? result
: recur(
arr,
current + 1,
count,
result.concat(
arr
[current % arr.length]//x value
[//y value
Math.floor(current / arr.length) %
(arr.length + 1)
],
),
);
return recur(arr, 0, count, []);
};
console.log(takeRoundRobin(22)(input));
本文标签: javascriptTake top X total items in a round robin from multiple arrays with RamdaStack Overflow
版权声明:本文标题:javascript - Take top X total items in a round robin from multiple arrays with Ramda - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745656281a2161616.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论