Reputation: 77
I'm guessing this is a basic question for anyone with experience and a logical brain, but this has stumped me for two days.
I'm trying to filter an array by states, then map just one property of the resulting array(s).
Here is my states array, which contains a subset of U.S. states:
const states = [{ state: 'AL' }, { state: 'OH' }, { state: 'PA' }]
Here is the array I want to first filter by state, and then map into a new array of just the values I need.
refData = [
{
state: 'AL',
details: [
{
code: '1A',
description: 'AL Description 1'
},
{
code: '1B',
description: 'AL Description 2'
},
{
code: '1C',
description: 'AL Description 3'
}
]
},
{
state: 'PA',
details: [
{
code: '1A',
description: 'PA Description 1'
},
{
code: '1B',
description: 'PA Description 2'
}
]
}
]
Here is my only working attempt to filter and then map, but it doesn't give me what I need:
const filteredRefData = refData
.filter((item) => (states.some(stateName => item.state === stateName.state)))
.map((item) => item.details)
What this gets me is an array of ALL the details, both code and description. What I need is JUST the description value, but no matter what I try, I can't arrive at that end result.
What I get from this map:
[
0: [
0: {code: "1A", description: "AL Description 1"}
1: {code: "1B", description: "AL Description 2"}
2: {code: "1C", description: "AL Description 3"}
],
1: [
0: {code: "1A", description: "PA Description 1"}
1: {code: "1B", description: "PA Description 2"}
]
]
What I need from this map:
[
0: [
0: "AL Description 1"
1: "AL Description 2"
2: "AL Description 3"
],
1: [
0: "PA Description 1"
1: "PA Description 2"
]
]
I tried using dynamic indexes, but that failed because any index I passed always related to the top level array, and not the nested array. I also tried the reduce method, but I found it difficult to understand how reduce works with just an object's key/value pairs.
Thank you for any help you can provide!
Upvotes: 0
Views: 1466
Reputation: 9568
You are right that this is a job for reduce
: a map
operation will yield a 1:1 translation of the input array (e.g. refData.map(state => state.state)
would return an array of the same length containing only the state
attribute), whereas reduce
can return an array with more or fewer results.
In your case, you start with x
number of states, but you will return y
descriptions, so map
is not an option, you must use reduce
(and this is a good example of how to use a functional approach).
For example, if you wanted to strip out just the state abbreviation using reduce
instead of map
, you could do that by accumulating the state abbreviation:
data.reduce(function(acc, x) {
return acc.concat(x.state);
}, []);
Here we set the initial value of the accumulator to an empty array []
(2nd arg) and we use concat
to append single values to the end of the acc
array -- the result is the same as using map
.
In your case, you want to concatenate an array of descriptions (or some other variable attribute), so here's how you could reduce the descriptions:
var refData = [
{
state: 'AL',
details: [
{
code: '1A',
description: 'AL Description 1'
},
{
code: '1B',
description: 'AL Description 2'
},
{
code: '1C',
description: 'AL Description 3'
}
]
},
{
state: 'PA',
details: [
{
code: '1A',
description: 'PA Description 1'
},
{
code: '1B',
description: 'PA Description 2'
}
]
}
];
refData.reduce(function(acc, x) {
var descriptions = x.details.map(function(detail) {
return detail.description;
});
return acc.concat(descriptions);
}, []);
or more tersely as
refData.reduce(function(acc, x) {
return acc.concat(x.details.map(detail => detail.description));
}, []);
Upvotes: 2
Reputation: 2371
If you insist on using only filter
and map
, try this:
const filteredRefData = refData
.filter((item) => (states.some(stateName => item.state === stateName.state)))
.map((item) => item.details.map(d => d.description))
But that's not really optimal solution. Maybe try using reduce
instead and compare results.
Upvotes: 1
Reputation: 18249
You were almost there. The only change needed was, in the final .map
, not to just return item.details
but the result of map
-ing that array to extract just the description:
const states = [{ state: 'AL' }, { state: 'OH' }, { state: 'PA' }];
const refData = [
{
state: 'AL',
details: [
{
code: '1A',
description: 'AL Description 1'
},
{
code: '1B',
description: 'AL Description 2'
},
{
code: '1C',
description: 'AL Description 3'
}
]
},
{
state: 'PA',
details: [
{
code: '1A',
description: 'PA Description 1'
},
{
code: '1B',
description: 'PA Description 2'
}
]
}
];
const filteredRefData = refData
.filter((item) => (states.some(stateName => item.state === stateName.state)))
.map((item) => item.details.map(({ description }) => description));
console.log(filteredRefData);
Upvotes: 3
Reputation: 255
I guess consuming the 'description' field would solve your problem. Try this:
const filteredRefData = refData
.filter((item) => (states.some(stateName => item.state === stateName.state)))
.map((item) => item.details.description)
Upvotes: 0