nurwadula
nurwadula

Reputation: 109

how to filter array object using object array

I have a filtering group js object like this :

 let filter = {
generalFilter:[
    {key: "connected", value: true},
    {key: "connected", value: false}
],
locationFilter:[
    {key: "deviceLocation", value: "def"},
     {key: "deviceLocation", value: "abc"}
],
paymentMethodsFilter:[
    {key: "devicePaymentMethods", value: "ab"}
]

}

and the main array like this:

const devices = [{
    deviceLocation: {
      label: "abc",
      value: "abc"
    },
    deviceName: "test7",
    devicePaymentMethods: [{
      label: "ab",
      value: "ab"
    }, {
      label: "cd",
      value: "cd"
    }, {
      label: "ef",
      value: "ef"
    }],
    deviceType: "iPad",
    id: "001",
    connected: true,
    enabled: true,
  },
  {
    deviceLocation: {
      label: "def",
      value: "def"
    },
    deviceName: "test4",
    devicePaymentMethods: [{
      label: "ab",
      value: "ab"
    }, {
      label: "cd",
      value: "cd"
    }],
    deviceType: "iPad",
    id: "004",
    connected: false,
    enabled: false,
  }
];

how can I filter this devices array using filter object? this is what I tried but it doesn't work

devices.filter((device) => {
    let shouldKeep = new Array(3)
    shouldKeep.fill(0) //fills the array with 0, meaning no filter groups has passed yet
    for(let filterGroupIndex in filter) {// Loop through each filter group in filter
        let filterGroup = filter[filterGroupIndex]
        for(let filterObject in filterGroup) { //Loop through
            if(filterGroup[filterObject].key in device && device[filterGroup[filterObject].key] === filterGroup[filterObject].value) {
                shouldKeep[filterGroupIndex] = 1 //Set current filterGroup to 1, meaning it has passed the filter
                break; // At least one value in that filter group is true, skip to next filter group
            }
        }
    }

    if(shouldKeep.reduce((a,b) => a + b, 0) >= filter.length) {
        return true;
    }
    return false
})

sometimes this filter object can be empty when its empty it should return full array without filter main array. what is the best approach to do this? I'm not sure about this maybe there is another solution for this. please help me

the default filter object is like this

let filter = {
    generalFilter:[],
    locationFilter:[],
    paymentMethodsFilter:[]

    }

the user can add an object to that filter array I'm using react hooks for example

 let filter = {
generalFilter:[
    {key: "connected", value: true},
    {key: "connected", value: false}
],
locationFilter:[],
        paymentMethodsFilter:[]}

then we have to check both connected: false and connected: true match data in the main array (if data has connected: true in the main array then it will show and also if data has connected: false it will show)

for this type of filter

let filter = {
generalFilter:[
    {key: "connected", value: true},
    {key: "connected", value: false}
],
locationFilter:[
    {key: "deviceLocation", value: "def"},
     {key: "deviceLocation", value: "abc"}
],
paymentMethodsFilter:[
    {key: "devicePaymentMethods", value: "ab"}
]

}

result should be

result =[{
    deviceLocation: {
      label: "abc",
      value: "abc"
    },
    deviceName: "test7",
    devicePaymentMethods: [{
      label: "ab",
      value: "ab"
    }, {
      label: "cd",
      value: "cd"
    }, {
      label: "ef",
      value: "ef"
    }],
    deviceType: "iPad",
    id: "001",
    connected: true,
    enabled: true,
  },
  {
    deviceLocation: {
      label: "def",
      value: "def"
    },
    deviceName: "test4",
    devicePaymentMethods: [{
      label: "ab",
      value: "ab"
    }, {
      label: "cd",
      value: "cd"
    }],
    deviceType: "iPad",
    id: "004",
    connected: false,
    enabled: false,
  }
];

because u can see in filter connected:true and connected :false and deviceLocation with def and abc and also devicePaymentMethods is ab

it's mean I want all devices with connected true and connected false with location abc and def and paymetmethod ab

Upvotes: 0

Views: 160

Answers (2)

Abhishek
Abhishek

Reputation: 1332

Check comments for explanation.

const filter = {
    generalFilter: [
        {key: "connected", value: true},
        {key: "connected", value: false}
    ],
    locationFilter: [
        {key: "deviceLocation", value: "def"},
        {key: "deviceLocation", value: "abc"}
    ],
    paymentMethodsFilter: [
        {key: "devicePaymentMethods", value: "ab"}
    ]
};

const parsedFilter = {};

/**
  converting filter into a hashmap with a filter as a Key and value as an 
  array of possible values.
  
  parsedFilter looks like this
 {"connected":[true,false],"deviceLocation":["def","abc"],"paymentOption":["ab"]}

  now we can easily check if any value is present in the filter.

   **/
const filterKeys = Object.keys(filter);

filterKeys.forEach((filterKey) => {
    if (Array.isArray(filter[filterKey])) {
        filter[filterKey].forEach((filterItem) => {
            if (Object.prototype.hasOwnProperty.call(parsedFilter, filterItem.key)) {
                parsedFilter[filterItem.key].push(filterItem.value);
            } else {
                parsedFilter[filterItem.key] = [filterItem.value];
            }
        });
    }
});


const devices = [
    {
        deviceLocation: {
            label: "abc",
            value: "abc"
        },
        deviceName: "test7",
        devicePaymentMethods: [
            {
                label: "ab",
                value: "ab"
            }, {
                label: "cd",
                value: "cd"
            }, {
                label: "ef",
                value: "ef"
            }],
        deviceType: "iPad",
        id: "001",
        connected: true,
        enabled: true,
    }
];

const result = [];

/**
  Looping through each device and check if that key is present in the 
  parsedFilter.
  
  if true: check for typeof value for that device key.
           if Object: then check if it is present in the array of not for the 
                      givem deviceKey in parsedFilter.
           if Array: then loop through each item and check if it is present 
                     in the parsedFilter for the given deviceKey
           if Number, string, boolean: Check if is present in the 
                      parsedFilter for the given deviceKey

  if false: simply add it to the result.
**/
if (Array.isArray(devices)) {
    devices.forEach((device) => {
        const keys = Object.keys(device);
        const resultDeviceObj = {};

        keys.forEach((key) => {
            if (Object.prototype.hasOwnProperty.call(parsedFilter, key)) {
                if (typeof device[key] === "object" && parsedFilter[key].includes(device[key].value)) {
                    resultDeviceObj[key] = device[key];
                } else if (Array.isArray(device[key])) {
                    const arrayResult = [];

                    device[key].forEach((item) => {
                        if (parsedFilter[key].includes(item.value)) {
                            arrayResult.push(item);
                        }
                    });

                    resultDeviceObj[key] = arrayResult;
                } else if(parsedFilter[key].includes(device[key])) {
                    resultDeviceObj[key] = device[key];
                }
            } else {
                resultDeviceObj[key] = device[key];
            }
        });

        result.push(resultDeviceObj);
    });
}

console.log("result", result);

Edit

const filter = {
    generalFilter: [{key: "connected", value: true}],
    locationFilter: [{key: "deviceLocation", value: "abcd"}],
    paymentMethodsFilter: [{key: "devicePaymentMethods", value: "ab"}]
};

const parsedFilter = {};

const filterKeys = Object.keys(filter);

filterKeys.forEach((filterKey) => {
    if (Array.isArray(filter[filterKey])) {
        filter[filterKey].forEach((filterItem) => {
            if (Object.prototype.hasOwnProperty.call(parsedFilter, filterItem.key)) {
                parsedFilter[filterItem.key][filterItem.value] = filterItem.value;
            } else {
                parsedFilter[filterItem.key] = {
                    [filterItem.value]: filterItem.value
                }
            }
        });
    }
});

//{"connected":{"true": true,"false": "false"},"deviceLocation":{"def": "def","abc": "abc"},"paymentOption":{"ab": "ab"}}
const devices = [{
    deviceLocation: {
        label: "abc",
        value: "abc"
    },
    deviceName: "test7",
    devicePaymentMethods: [{
        label: "ab",
        value: "ab"
    }, {
        label: "cd",
        value: "cd"
    }, {
        label: "ef",
        value: "ef"
    }],
    deviceType: "iPad",
    id: "001",
    connected: true,
    enabled: true,
},
    {
        deviceLocation: {
            label: "def",
            value: "def"
        },
        deviceName: "test4",
        devicePaymentMethods: [{
            label: "ab",
            value: "ab"
        }, {
            label: "cd",
            value: "cd"
        }],
        deviceType: "iPad",
        id: "004",
        connected: false,
        enabled: false,
    }
];

const result = [];

const isObject = function (a) {
    return a.constructor.toString().indexOf("Object") !== -1;
};

if (Array.isArray(devices)) {
    devices.forEach((device) => {
            const keys = Object.keys(device);
            let isValid = true;
            for (let i = 0; i < keys.length; i++) {
                const key = keys[i];

                if (Object.prototype.hasOwnProperty.call(parsedFilter, key)) {
                    if (isObject(device[key]) &&
                        !Object.prototype.hasOwnProperty.call(parsedFilter[key], device[key].value)) {
                        isValid = false;
                        break;
                    } else if (Array.isArray(device[key])) {
                        isValid = false;
                        for (let j = 0; j < device[key].length; j++) {
                            const item = device[key][j];
                            if (Object.prototype.hasOwnProperty.call(parsedFilter[key], item.value)) {
                                isValid = true;
                                break;
                            }
                        }

                        if (!isValid) {
                            break;
                        }
                    } else if (typeof device[key] === "boolean" &&
                        !Object.prototype.hasOwnProperty.call(parsedFilter[key], device[key])) {
                        isValid = false;
                        break;
                    }
                }
            }

            if (isValid) {
                result.push(device);
            }
        }
    );
}


console.log("result", result)

Upvotes: 1

aleksa_95
aleksa_95

Reputation: 259

Can you explain more what you are trying to do. It is very unclear from your question.

In general you would do something like this if you want to filter by device location.

devices.filter(device=>device.deviceLocation.label==="abc")

I hope this example will help you figure out the rest.

Upvotes: 0

Related Questions