1

I have the following object:

{
  "id": 2,
  "name": "tes name",
  "status": 0,
  "categories": [
    {
      "category_id": 1,
      "name": "taxes",
      "description": "taxes",
      "keys": [
        {
          "key_id": 1,
          "value": "preset unique value if not overridden, real text value"
        },
        {
          "key_id": 2,
          "value": "test name"
        }
      ]
    },
    {
      "category_id": 2,
      "name": "surcharges",
      "description": "surcharges",
      "keys": [
        {
          "key_id": 3,
          "value": "preset value if not overridden, real text value"
        },
        {
          "key_id": 5,
          "value": ""
        }
      ]
    },
    {
      "category_id": 3,
      "name": "errors",
      "description": "errors",
      "keys": [
        {
          "key_id": 6,
          "value": "preset value if not overridden, real text value"
        },
        {
          "key_id": 10,
          "value": "unique value to test search"
        }
      ]
    }
  ]
}

What I need to do is update the object based on: it must contain the key in the keys array if only key.value contains the string 'unique' otherwise the array must not contain that key in the keys array.

Expected output is:

{
  "id": 2,
  "name": "tes name",
  "status": 0,
  "categories": [
    {
      "category_id": 1,
      "name": "taxes",
      "description": "taxes",
      "keys": [
        {
          "key_id": 1,
          "value": "preset unique value if not overridden, real text value"
        }
      ]
    },
    {
      "category_id": 3,
      "name": "errors",
      "description": "errors",
      "keys": [
        {
          "key_id": 10,
          "value": "unique value to test search"
        }
      ]
    }
  ]
}

I tried nested filter but it won't work for me, always getting the same object as it is.

my code:

var result = obj.categories.filter(function(category) {
    return category.keys.filter(function(key) {
        return key.value.includes(action.payload)
    });
});

Any suggestions or where am I going wrong?

6
  • filter requires you to return either true or false, to filter whether to keep the value or not. Commented Feb 8, 2019 at 11:40
  • do you need a all new object of the result, without mutating the original data? Commented Feb 8, 2019 at 11:40
  • @NinaScholz yes all new object, original data not to be mutated. Commented Feb 8, 2019 at 11:41
  • @George key.value.includes(action.payload) i am using this for true / false Commented Feb 8, 2019 at 11:42
  • yes but not for the first filter, you are returning an array, that is not true or false. Commented Feb 8, 2019 at 11:45

3 Answers 3

1

You could take a nested approach and reduce the array by checking the nested filterd array.

This solution does not mutate the original data.

var data = { id: 2, name: "tes name", status: 0, categories: [{ category_id: 1, name: "taxes", description: "taxes", keys: [{ key_id: 1, value: "preset unique value if not overridden, real text value" }, { key_id: 2, value: "test name" }] }, { category_id: 2, name: "surcharges", description: "surcharges", keys: [{ key_id: 3, value: "preset value if not overridden, real text value" }, { key_id: 5, value: "" }] }, { category_id: 3, name: "errors", description: "errors", keys: [{ key_id: 6, value: "preset value if not overridden, real text value" }, { key_id: 10, value: "unique value to test search" }] }] },
    filtered = Object.assign({}, data, {
        categories: data.categories.reduce((r, o) => {
            var keys = o.keys.filter(({ value }) => value.includes('unique'));
            if (keys.length) {
                r.push(Object.assign({}, o, { keys }));
            }
            return r;
        }, [])
    });

console.log(filtered);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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

Comments

0

Hope this works for you.

Also filter is not in place method, so even though you are filtering Categories.keys and returning it , it wont be modified.

Once filtered, we are using categories.keys.length to filter categories with empty keys

var obj = {
  "id": 2,
  "name": "tes name",
  "status": 0,
  "categories": [
    {
      "category_id": 1,
      "name": "taxes",
      "description": "taxes",
      "keys": [
        {
          "key_id": 1,
          "value": "preset unique value if not overridden, real text value"
        },
        {
          "key_id": 2,
          "value": "test name"
        }
      ]
    },
    {
      "category_id": 2,
      "name": "surcharges",
      "description": "surcharges",
      "keys": [
        {
          "key_id": 3,
          "value": "preset value if not overridden, real text value"
        },
        {
          "key_id": 5,
          "value": ""
        }
      ]
    },
    {
      "category_id": 3,
      "name": "errors",
      "description": "errors",
      "keys": [
        {
          "key_id": 6,
          "value": "preset value if not overridden, real text value"
        },
        {
          "key_id": 10,
          "value": "unique value to test search"
        }
      ]
    }
  ]
}

var result = obj.categories.filter(function(category) {
      category['keys']=category.keys.filter(function(key) {
       return key.value.includes('unique')
    })
    return category['keys'].length
});

console.log(result)

Comments

0

The output of the function given to filter() should always return a boolean that indicates if the value should be filtered out or not. Your code

return category.keys.filter(function(key) {
    return key.value.includes(action.payload)
});

returns the filtered key lists, but then the outer filter function uses these lists as the filter value. As long as the filtered list contains any element, it will be converted to true and therefore the category will not be filtered out.

The actual code should instead look something like this:

obj.categories.forEach(function(category){
    category.keys = category.keys.filter(function(key){
        return key.value.includes(action.payload);
    });
});

This iterates over all key arrays and applies the filter to each one.

If you also want to filter out categories that would then have empty key array, you can add the outer filter back in but keep the assignment to category.keys:

obj.categories = obj.categories.filter(function(category){
    return (category.keys = category.keys.filter(function(key){
        return key.value.includes(action.payload);
    }););
});

This works because the return value of the assignment is the value that is assigned to category.keys, so the filter will once again keep only those categories that still contain keys.

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.