1

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!

4 Answers 4

3

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);

Sign up to request clarification or add additional context in comments.

1 Comment

Robin, thank you SO much...that did it! I tried chaining the map method a couple ways, but not in that (correct) way. Much gratitude!!
2

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));
}, []);

1 Comment

Great! Thank you...I'll try this, especially since I'm using this value to map menu items which are, themselves, part of a Formik FieldArray. So, there a lot of layers to think through for someone like me (more of a design background than development...)
1

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.

1 Comment

Thanks! I'll try this, too.
0

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)

1 Comment

Thanks...I tried that, but because details is an array itself, it requires an index to reach its object's properties. Adding description returns an undefined.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.