1

In the snippet below, before return(res) , I log it, and it's not undefined

but somehow, it's being returned as undefined.

What am I doing wrong?

filterData = (inputData, searchedKey) => {
  inputData.forEach((data, index) => {
    if(data){
      if(data.hasOwnProperty(searchedKey)){
        const res = data[searchedKey]
        console.log(res) /// logs the results
        return(res) /// returns undefined
      }
      var dataToProcess = [];

      var fieldKeys = Object.keys(data)
      fieldKeys = fieldKeys.filter((field, index) => !field.includes("#"))
      fieldKeys.forEach((key, index) => {
        dataToProcess.push(data[key]);
      })
      this.filterData(dataToProcess, searchedKey)
    }
  })
}

console.log(this.filterData([{"#name": "foo", "#type": "bar"}], "#type"))

4
  • 1
    how exactly do you plan to 'return' from a forEach ? Commented Jun 8, 2017 at 9:59
  • filterData is not returning anything, so logically that prints undefined Commented Jun 8, 2017 at 10:01
  • what return(res) is? a function invocation with res as input? where is the return function? please supplement your code, or rather use the return keyword edit: see @Dellirium 's comment, you cannot return from foreach edit2: gosh, i didnt know its a valid return statement: return(something) Commented Jun 8, 2017 at 10:02
  • Returning from forEach has no effect on container function. Use for or for...of loop instead of forEach if you want to immediately return from a function Commented Jun 8, 2017 at 10:05

2 Answers 2

1

Some issues:

  • forEach does not return anything else than undefined, so returning a value in its callback function does not do anything useful.
  • Your function does not return a value
  • The return value of the recursive call is not used.
  • if (data) is not good enough to make sure data is an object. For instance, it would also be true for a non-zero number. Use Object(data) === data instead.
  • Since there could be multiple matches (in a nested input object), your function should return an array, which is also what someone would expect when seeing your function name. Also the standard array filter method returns an array. So it would be in line with that.

Here is how you could make it work:

var filterData = (inputData, searchedKey) => {
    inputData = inputData.filter( data => Object(data) === data );
    return !inputData.length ? [] :
        inputData.filter( data => data.hasOwnProperty(searchedKey) )
            .map( data => data[searchedKey] )
            // Add the results from recursion:
            .concat(filterData([].concat(...
                inputData.map( data =>
                    Object.keys(data)
                        .filter( key => !key.startsWith("#") )
                        .map( key => data[key] )
                )
            ), searchedKey));
};

var data = [{
    "#name": "foo", 
    "#title": "mr",
    "deeper": [{
        "#nope": "bad",
        "deepest": [{
            "nothing_here": null,
            "#type": "deeper bar",
        }]
    }, {
        "#type": "bar",
    }]
}];

console.log(filterData(data, "#type"));

If you need only the first match, then use this variant:

var filterData = (inputData, searchedKey) => {
    inputData = inputData.filter( data => Object(data) === data );
    var res = inputData.find( data => data.hasOwnProperty(searchedKey) );
    return res !== undefined ? res[searchedKey] :
        // Try in nested objects:
        filterData([].concat(...
            inputData.map( data =>
                Object.keys(data)
                    .filter( key => !key.startsWith("#") )
                    .map( key => data[key] )
            )
        ), searchedKey);
};

var data = [{
    "#name": "foo", 
    "#title": "mr",
    "deeper": [{
        "#nope": "bad",
        "deepest": [{
            "nothing_here": null,
            "#type": "deeper bar",
        }]
    }, {
        "#type": "bar",
    }]
}];

console.log(filterData(data, "#type"));

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

2 Comments

Thanks for your answer. It helped me a lot. But I need to return as soon as I find the first match, I don't need more than one result. there are gonna be many matches, I just need the first match. How can I achieve that? Thanks.
You're welcome. I added solution for getting first match.
0

Is this what you want to achieve?

filterData = (inputData, searchedKey) => {
  return inputData.map((data, index) => {
    if(data){
      if(data.hasOwnProperty(searchedKey)){
        const res = data[searchedKey]
        console.log(res) /// logs the results
        return(res) /// returns undefined
      }
      var dataToProcess = [];

      var fieldKeys = Object.keys(data)
      fieldKeys = fieldKeys.filter((field, index) => !field.includes("#"))
      fieldKeys.forEach((key, index) => {
        dataToProcess.push(data[key]);
      })
      this.filterData(dataToProcess, searchedKey)
    }
  })
}

console.log(this.filterData([{"#name": "foo", "#type": "bar"}], "#type"))

Use Array#map(), it's pretty useful in many cases.

Comments

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.