I've been trying to create a generic partition function that returns an array of arrays. the function should be made under the following guidelines:
Arguments:
- An array
- A function
Objectives:
Call <function> for each element in <array> passing it the arguments:
element, key, <array>
Return an array that is made up of 2 sub arrays:
0. An array that contains all the values for which <function> returned something truthy
1. An array that contains all the values for which <function> returned something falsy
Here is what I have so far. I get the return of two. I feel like maybe I just have to do the filter function on two separate occasions, but I'm not sure how to put it together. Thoughts and suggestions are highly appreciated.
_.partition = function (collection, test){
var allValues = [];
var matches = [];
var misMatches = [];
_.filter(collection.value, function(value, key, collection){
if (test(value[key], key, collection) === "string"){
matches.push(value[key]);
}else{
misMatches.push(value[key]);
}
});
return allValues.push(matches, misMatches);
}
I've been trying to create a generic partition function that returns an array of arrays. the function should be made under the following guidelines:
Arguments:
- An array
- A function
Objectives:
Call <function> for each element in <array> passing it the arguments:
element, key, <array>
Return an array that is made up of 2 sub arrays:
0. An array that contains all the values for which <function> returned something truthy
1. An array that contains all the values for which <function> returned something falsy
Here is what I have so far. I get the return of two. I feel like maybe I just have to do the filter function on two separate occasions, but I'm not sure how to put it together. Thoughts and suggestions are highly appreciated.
_.partition = function (collection, test){
var allValues = [];
var matches = [];
var misMatches = [];
_.filter(collection.value, function(value, key, collection){
if (test(value[key], key, collection) === "string"){
matches.push(value[key]);
}else{
misMatches.push(value[key]);
}
});
return allValues.push(matches, misMatches);
}
Share
Improve this question
edited Jan 25, 2016 at 8:31
Mr Lister
46.6k15 gold badges113 silver badges155 bronze badges
asked Jan 19, 2016 at 22:15
JesusHBruhnkeJesusHBruhnke
331 silver badge5 bronze badges
8
- there is a formatting iussue in your request that makes it difficult to read it. – Marco Altieri Commented Jan 19, 2016 at 22:17
- Looking at it now, I know my original thought was to filter the strings into one array and the numbers into another array, yet when I test it, it returns just a number. So somewhere my logic is off. More than likely it is something very simple. – JesusHBruhnke Commented Jan 19, 2016 at 22:18
- 1 can you show us a sample input+desired output? – dandavis Commented Jan 19, 2016 at 22:20
-
1
try
return allValues.push(matches, misMatches), allValues;
– dandavis Commented Jan 19, 2016 at 22:29 -
1
In your question text, you say returned something truthy, but in your sample code you are paring the returned value to
"string"
--which is correct? – user663031 Commented Jan 20, 2016 at 3:15
4 Answers
Reset to default 5Here is a version which uses reduce
:
function partition(arr, filter) {
return arr.reduce(
(r, e, i, a) => {
r[filter(e, i, a) ? 0 : 1].push(e);
return r;
}, [[], []]);
}
Here's an alternative version which uses Array#filter
to find the matches, and builds an array of non-matches as it goes along:
function partition(arr, filter) {
var fail = [];
var pass = arr.filter((e, i, a) => {
if (filter(e, i, a)) return true;
fail.push(e);
});
return [pass, fail];
}
And a typed TypeScript variant:
function partition<T>(arr: T[], filter: (el: T, i: number, a: T[]) => boolean) {
const fail: T[] = [];
const pass = arr.filter((e, i, a) => {
if (filter(e, i, a)) return true;
fail.push(e);
});
return [pass, fail];
}
You're correct about calling the filter
method on separate occasions. One filter
call would obtain the truthy values; the other would obtain the falsy values:
_.partition = function(collection, testFunc) {
var matches = collection.filter(function(elem) {
return test(elem) === 'string';
});
var misMatches = collection.filter(function(elem) {
return test(elem) !== 'string';
});
return [matches, misMatches];
}
You are close, but there are a couple issues I see:
- You are returning the result of
allValues.push
which is notallValues
itself, but rather the new length of the array. - You are using
_.filter
to iterate over array elements and sort them into two arrays. This is strange, since it's not the intended use of_.filter
.
If you want a quick and readable solution using _.filter
, this will work:
_.mixin({
partition: function(collection, test) {
return [
_.filter(collection, test), // items which satisfy condition
_.filter(collection, _.negate(test)) // items which don't
];
}
});
A more efficient solution which makes only one pass over the collection is below (this is almost what you already have):
_.mixin({
partition: function(collection, test) {
var matches = [], misMatches = [], value;
// can replace this loop with _.each
for (var i = 0, len = collection.length; i < len; ++i) {
value = collection[i];
// push the value into the appropriate array
if (test(value, i, collection)) {
matches.push(value);
} else {
misMatches.push(value);
}
}
return [matches, misMatches];
}
});
Usage examples (and Plunker):
function isOdd(x) {
return x % 2;
}
// _.mixin allows you to do either one of these
_.partition([1, 2, 3, 4, 5, 6], isOdd); // result: [[1, 3, 5], [2, 4, 6]]
_([1, 2, 3, 4, 5, 6]).partition(isOdd); // result: [[1, 3, 5], [2, 4, 6]]
// this is a use case you brought up in the ments
_.partition([1, "a", 2, "b", 3, "c"], _.isString); // result: [["a", "b", "c"], [1, 2, 3]]
This is generally known as partition
ing in functional languages. You suply an array (xs
) and a predicate function (p
) to a reduce
ing function with initial value [[],[]]
.
var partition = (xs,p) => xs.reduce( (r,e) => ( p(e) ? r[0].push(e)
: r[1].push(e)
, r
)
, [[],[]]
);
Such that;
> partition([1,2,3,4,5,6,7,8,9,0], x => x < 5)
> [[1, 2, 3, 4, 0],[5, 6, 7, 8, 9]]