1

Given that I have a JSON structure like this:

{
    "firstData": [{
        "secondData": [{
            "thirdData": [{
                "value": "whatever"
            }]
        }]
    }]
}

And I need to map from thirdData value === "whatever"

So I am doing

 const result = firstData.map(first => {
            return first.secondData.map(second => {
                return second.thirdData.map(third => {
                    return third.value === 'whatever';
                });
            });
        });

And this works somewhat fine, but the result is a another deeply nested array (like [ [ [ {results..} ] ] ]). I know I can flatten this to a single array by other means, but I feel like I am miss using .map(). How can I modify this result to a single array that contains the values of thirdData where the value is what ever I want?

The desired result for this would be a single array of thirdData objects:

[{ value: 'whatever'}, ... {n}]
2
  • 2
    Could you add in the result you're expecting to get? Commented Aug 8, 2018 at 9:41
  • Array.flapMap() instead of Array.map() if it's supported. Else just flatten the result yourself with something like .reduce(). Commented Aug 8, 2018 at 9:42

2 Answers 2

4

You can use Array#reduce for reducing into a single value(in this case single array) and Array#forEach for iterating over the nested array.

const data = {
  "firstData": [{
    "secondData": [{
      "thirdData": [{
        "value": "whatever"
      }]
    }]
  }]
}

const result = data.firstData.reduce((arr, first) => {
  // iterate over the second level array
  first.secondData.forEach(second => {
    // iterate over the third level array
    second.thirdData.forEach(third => {

      // push the value into the result array,
      // change here, in case you want the value 
      //arr.push(third.value === 'whatever');
      
      // in case you need the object then do it like
      if(third.value === 'whatever') arr.push(third);

    });
  });
  // return the array reference for the next iteration
  return arr;

  // set the initial value as an array for the result
}, []);

console.log(result);

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

3 Comments

@Keith : me also not sure, but the output of his code is [[[true]]]]
@Keith : if he needs the value, then just need to update following line arr.push(third.value === 'whatever');
Indeed, this is the correct answer for the OP.. :),.. My brain must be in reverse today.. :) I blame in on the drugs I'm having, must have side effects..
3

If you want a flat result, this isn't a use case for map. The simple solution is just to use an array you close over and push to:

const result = [];
firstData.forEach(first => {
    return first.secondData.forEach(second => {
        result.push(...second.thirdData.filter(third => third.value === 'whatever'));
    });
});

Live Example with a slight extension to your minimal provided data:

const data = {
    "firstData": [{
            "secondData": [{
                "thirdData": [{
                        "value": "whatever",
                        "label": "third #1.1"
                    },
                    {
                        "value": "whatever",
                        "label": "third #1.2"
                    },
                    {
                        "value": "unrelated",
                        "label": "unrelated"
                    }
                ]
            }]
        },
        {
            "secondData": [{
                "thirdData": [{
                        "value": "another unrelated"
                    },
                    {
                        "value": "whatever",
                        "label": "third #2"
                    }
                ]
            }]
        }
    ]
};
const result = [];
data.firstData.forEach(first => {
    return first.secondData.forEach(second => {
        result.push(...second.thirdData.filter(third => third.value === 'whatever'));
    });
});
console.log(result);
.as-console-wrapper {
  max-height: 100% !important;
}

Note the filter on the thirdData and using spread notation to push that data into result.

That assumes you want the entry from thirdData that has .value === 'whatever' rather than a true/false. If you want the true/false instead, change that filter to map.

Or the for-of equivalent:

const result = [];
for (const first of firstData) {
    for (const second of first.secondData) {
        result.push(...second.thirdData.filter(third => third.value === 'whatever'));
    }
}

Live Example with a slight extension to your minimal provided data:

const data = {
    "firstData": [{
            "secondData": [{
                "thirdData": [{
                        "value": "whatever",
                        "label": "third #1.1"
                    },
                    {
                        "value": "whatever",
                        "label": "third #1.2"
                    },
                    {
                        "value": "unrelated",
                        "label": "unrelated"
                    }
                ]
            }]
        },
        {
            "secondData": [{
                "thirdData": [{
                        "value": "another unrelated"
                    },
                    {
                        "value": "whatever",
                        "label": "third #2"
                    }
                ]
            }]
        }
    ]
};
const result = [];
for (const first of data.firstData) {
    for (const second of first.secondData) {
        result.push(...second.thirdData.filter(third => third.value === 'whatever'));
    }
}
console.log(result);
.as-console-wrapper {
  max-height: 100% !important;
}

(Same note about filter/map.)

As with all array operations, you can shoehorn this into reduce, and I guarantee you you'll get answers primarily using reduce, but there's no good reason to use reduce here.

const result = firstData.reduce((result, first) => {
    return first.secondData.reduce((result, second) => {
        result.push(...second.thirdData.filter(third => third.value === 'whatever'));
        return result;
    }, result);
}, []);

Again, though, there's no good reason for that. It's just more complicated.

Live Example with a slight extension to your minimal provided data:

const data = {
    "firstData": [{
            "secondData": [{
                "thirdData": [{
                        "value": "whatever",
                        "label": "third #1.1"
                    },
                    {
                        "value": "whatever",
                        "label": "third #1.2"
                    },
                    {
                        "value": "unrelated",
                        "label": "unrelated"
                    }
                ]
            }]
        },
        {
            "secondData": [{
                "thirdData": [{
                        "value": "another unrelated"
                    },
                    {
                        "value": "whatever",
                        "label": "third #2"
                    }
                ]
            }]
        }
    ]
};
const result = data.firstData.reduce((result, first) => {
    return first.secondData.reduce((result, second) => {
        result.push(...second.thirdData.filter(third => third.value === 'whatever'));
        return result;
    }, result);
}, []);
console.log(result);
.as-console-wrapper {
  max-height: 100% !important;
}

(Same note about filter/map.)

5 Comments

Yes, this is what I am doing now but I am just curious how to do this without defining an result array outside of the loop.
You could use reduce and declare the result array as the initial value.
@tjugg - You could abuse reduce, but there's no good reason to.
This won't work with supplied data,.. notices he's array inside array.. :)
@Keith - What specifically do you think won't work? This does indeed expect arrays within arrays within arrays.

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.