Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a page that Filters data on multiple conditions based on user input

Tags:

reactjs

The idea was to create an advanced search page that would enable users to filter data as per their choice by selecting as many as five options to narrow down their search. The results would then later get displayed on a table. Having said that, a user may decide to filter by either using only a single option i.e By state, or by taking multiple options i.e By Name, By District, and By Category or as said earlier up to 5 options. However, I have no way of figuring out how this can be done. You would find screenshots followed by the code below:

A scenario where the user chooses to filter using the By State option

enter image description here

Scenario, where the options are chosen are multiple to narrow down the search

enter image description here

The code is as follows:

    const AdvanceFilterScreen = () => {
    const [byNameFilter, setByNameFilter] = useState(false);
    const [name, setName] = useState('');

    const [byDateFilter, setByDateFilter] = useState(false);

    const [byCategoryFilter, setByCategoryFilter] = useState(false);
    const [category, setCategory] = useState('');

    const [byDistrictFilter, setByDistrictFilter] = useState(false);
    const [district, setDistrict] = useState('');

    const [byStateFilter, setByStateFilter] = useState(false);
    const [status, setStatus] = useState('');

    const [bySerialNumberFilter, setBySerialNumberFilter] = useState(false);
    const [serialNumber, setSerialNumber] = useState('');

    const [byIssueFilter, setbyIssueFilter] = useState(false);

    const [buttonValue, setFilterButtonValue] = useState('');

    //What the following methods do is set the value of setFilterButtonValue to '' so that 
    //the value of the current selected option turns to blank upon de-selecting them
    //On-Click methods that store the values to choiceArray
    const nameFilter = () => {
        setByNameFilter(prev => !prev);
        setFilterButtonValue('');
        setName('')
    }

    const categoryFilter = () => {
        setByCategoryFilter(prev => !prev);
        setFilterButtonValue('');
        setCategory('')
    }

    const districtFilter = () => {
        setByDistrictFilter(prev => !prev);
        setFilterButtonValue('');
        setDistrict('')
    }

    const stateFilter = () => {
        setByStateFilter(prev => !prev);
        setFilterButtonValue('');
        setStatus('')
    }

    const serialNumberFilter = () => {
        setBySerialNumberFilter(prev => !prev);
        setFilterButtonValue('');
        setSerialNumber('')
    }

    return (
        <div className="advanceFilterScreen">
            <div className="navBarFilter">
                <NavBar></NavBar>
            </div>
            <div className="chooseFilters">
                <div className="chooseColumn">
                    <div className="caption">Choose Filters (Upto 5 filters at a time)</div>
                    <div className="filterOptions">
                        <div onClick={nameFilter}>
                            <div className={byNameFilter ? "nameSelectedFilter" : "byNameFilter"}>By Name</div>
                        </div>

                        <div onClick={() => setByDateFilter(prev => !prev)}>
                            <div className={byDateFilter ? "dateSelectedFilter" : "byDateFilter"}>By Date</div>
                        </div>

                        <div onClick={categoryFilter}>
                            <div className={byCategoryFilter ? "categorySelectedFilter" : "byCategoryFilter"}>By Category</div>
                        </div>

                        <div onClick={districtFilter}>
                            <div className={byDistrictFilter ? "districtSelectedFilter" : "byDistrictFilter"}>By District</div>
                        </div>

                        <div onClick={stateFilter}>
                            <div className={byStateFilter ? "stateSelectedFilter" : "byStatesFilter"}>By States</div>
                        </div>

                        <div onClick={serialNumberFilter}>
                            <div className={bySerialNumberFilter ? "serialNumberSelectedFilter" : "bySerialNumberFilter"}>By Serial Number</div>
                        </div>

                        <div onClick={() => setbyIssueFilter(prev => !prev)}>
                            <div className={byIssueFilter ? "issueSelectedFilter" : "byIssueFilter"}>By Issue</div>
                        </div>
                    </div>
                </div>
            </div>

            <div className={(byNameFilter || byDateFilter || byCategoryFilter || byDistrictFilter || byStateFilter || bySerialNumberFilter || byIssueFilter) ? "filterDropdowns" : "noFilter"}>
                <div className="filterClass">
                    {byNameFilter ? <div style={{ marginRight: "15px" }}>
                        <NameFilter setName={setName}></NameFilter>
                    </div> : null}
                    {byDateFilter ? <div style={{ marginRight: "15px" }}>Search By Date</div> : null}
                    {byCategoryFilter ? <div style={{ marginRight: "15px" }}>
                        <CategoryFilter setCategory={setCategory}></CategoryFilter>
                    </div> : null}
                    {byDistrictFilter ? <div style={{ marginRight: "15px" }}>
                        <DistrictFilter setDistrict={setDistrict}></DistrictFilter>
                    </div> : null}
                    {byStateFilter ? <div style={{ marginRight: "15px" }}>
                        <StatusFilter setStatus={setStatus}></StatusFilter>
                    </div> : null}
                    {bySerialNumberFilter ? <div style={{ marginRight: "15px" }}>
                        <SerialNumberFilter setSerialNumber={setSerialNumber}></SerialNumberFilter>
                    </div> : null}
                    {byIssueFilter ? <div>Search By Issue</div> : null}
                </div>
                <div>
                    <FilterSearchButton name={name} category={category} district={district} status={status} serialNumber={serialNumber} setFilterButtonValue={setFilterButtonValue}></FilterSearchButton>
                </div>
            </div>
            <div style={{ marginTop: "50px" }}>
                {district ? <DistrictTable district={district}></DistrictTable> : null}
                <Table buttonValue={buttonValue}></Table>
            </div>
        </div>
    );
};

export default AdvanceFilterScreen;

FilterSearchButton component This is where the filtering occurs using the props received from the selected options. Right now, this component is configured only to filter using one prop value (i.e either using the name, category, district props etc.). What should I do to filter using multiple conditions using? Just to remind you again, the selected options have been passed on as props to this component.

    const FilterSearchButton = ({ name, category, district, status, serialNumber, setFilterButtonValue }) => {
    const [multiFilter, setMultiFilter] = useState([]);

    useEffect(() => {
        setMultiFilter(dummyData.flatMap(value => value.ward_no.flatMap(
            valueArray => ['grievances', 'general', 'urgent', 'services'].flatMap(
                value => valueArray[value]))).filter(value => value.name === name ||
                    value.districtName === district ||
                    value.type === category ||
                    value.status === status ||
                    value.serial_number === serialNumber
                )
        )
    }, [name, category, district, status, serialNumber, setFilterButtonValue]);

    const searchFilter = () => {
        setFilterButtonValue(multiFilter)
    }

    return (
        <div className="searchFilterButton" onClick={searchFilter}>Search</div>
    );
}

export default FilterSearchButton;

P.S: dummyData is a JSON file on which the filtering occurs. The ['grievances', 'general', 'urgent', 'services'] represents the keys over which the filtering occurs. An extract of the JSON data is below. I have removed most of the properties to keep them short.

[
    {
        "district": "Kolkata",
        "ward_no": [
            {
                "ward": "6",
                "grievance": [
                    {
                        "serial_number": "0001",
                        "name" : "Mr.A"
                    },
                    {
                        "serial_number": "0002",
                        "name" : "Mr.B"
                    }
                ],
                "general": [
                    {
                        "serial_number": "0003",
                        "name" : "Mr.C"
                    },
                    {
                        "serial_number": "0004",
                        "name" : "Mr.D"
                    }
                ]
            },
            {
                "ward": "7",
                "grievance": [
                    {
                        "serial_number": "0005",
                        "name" : "Mr.E"
                    },
                    {
                        "serial_number": "0006",
                        "name" : "Mr.F"
                    }
                ],
                "general": [
                    {
                        "serial_number": "0007",
                        "name" : "Mr.G"
                    },
                    {
                        "serial_number": "0008",
                        "name" : "Mr.H"
                    }
                ]
            }
        ]
    },
    {
        "district": "Hooghly",
        "ward_no": [
            {
                "ward": "8",
                "grievance": [
                    {
                        "serial_number": "0009",
                        "name" : "Mr.I"
                    },
                    {
                        "serial_number": "0010",
                        "name" : "Mr.J"
                    }
                ],
                "general": [
                    {
                        "serial_number": "0011",
                        "name" : "Mr.K"
                    },
                    {
                        "serial_number": "0012",
                        "name" : "Mr.L"
                    }
                ]
            },
            {
                "ward": "9",
                "grievance": [
                    {
                        "serial_number": "0013",
                        "name" : "Mr.M"
                    },
                    {
                        "serial_number": "0014",
                        "name" : "Mr.N"
                    }
                ],
                "general": [
                    {
                        "serial_number": "0015",
                        "name" : "Mr.O"
                    },
                    {
                        "serial_number": "0018",
                        "name" : "Bruno Fernandes"
                    }
                ]
            }
        ]
    }
]
like image 977
coolhack7 Avatar asked Dec 02 '21 18:12

coolhack7


People also ask

Which filter type is used to filter data based on the multiple condition?

If the data you want to filter requires complex criteria (such as Type = "Produce" OR Salesperson = "Davolio"), you can use the Advanced Filter dialog box.

How to filter data based on cell values that contain text?

Filter Data Based on Cell Values that Contain Text Let’s assume that we have a dataset of products with their sales persons’ names, joining dates, and total sales. Now we will filter data based on the salesperson’s name. There are two additional functions used in the formula. They are ISNUMBER Click on the function’s name to learn more about them.

How to get the number of filters applied to a column?

'Number of filters to apply numberOfFilters = InputBox ("Number of filters to apply to Column " & colNumber) Redim filters (0 to numberOfFilters - 1) As String '<-- resize according to user input Thanks for contributing an answer to Stack Overflow!

Why do we need to filter pandas Dataframe with multiple conditions?

The reason is dataframe may be having multiple columns and multiple rows. Selective display of columns with limited rows is always the expected view of users. To fulfill the user’s expectations and also help in machine deep learning scenarios, filtering of Pandas dataframe with multiple conditions is much necessary.

How to create filterable div elements?

Create Filterable DIV Elements 1 Step 1) Add HTML:#N#Example#N#<!-- Control buttons -->#N#<div id="myBtnContainer">#N#<button class="btn active"... 2 Step 2) Add CSS:#N#Example#N#.container {#N#overflow: hidden;#N#}#N#.filterDiv {#N#float: left;#N#background-color: 3 2196F3;#N#color:... 4 Step 3) Add JavaScript: More ...


Video Answer


3 Answers

This might have a performance impact if your list is really big, but since .filter() returns back an array, why not chain 5 filters together.

For example,

let a = [1,2,3,4,5,6,7,8,9,10]
console.log(a.filter(b => b > 0).filter(c => c > 1).filter(d => d > 2).filter(e => e > 3))

This would print the array - [4,5,6,7,8,9,10]

Wrap you filter logic in a useEffect and so whenever an input changes for the props, the useEffect can run again. Maybe you could improve it by using useCallback as well.

I hope you get the idea and can use it for your solution

like image 190
LoXatoR Avatar answered Nov 15 '22 08:11

LoXatoR


try to do a filter that you will add to him more arguments when you start to select more stuff

and then you send it a method that will do something like that

const filterHandler =(data)=>{
  let flag = true;
  if(bySerialNumberFilter){
    flag = data?.ward_no[0]?.grievance?.serial_number  === serialNumber
  }
  return flag;
}

in the data you pass the value from the filter and like this you do if to each of the values that the byState is true then you compare it like that you can add more search on the fly the flag is initial as true because by default you want it to be display you can put it as no but then you need to check if you dont put any filter on

like image 21
TalOrlanczyk Avatar answered Nov 15 '22 06:11

TalOrlanczyk


const [filteredData,setFilteredData] = useState([]) //render this list
const [name, setName] = useState('');
const [status,setStatus] = useState('');
const [district, setDistrict] = useState('');
const [status, setStatus] = useState('');

const data=[......]  //original data

const checkInStatus =(obj,value,key)=>{
 const statusList = ['grievances', 'general', 'urgent', 'services'];
 for (let status in statusList){
   if(obj?.[status]?.[key] === value) return true
 }
 return false
}

useEffect(()=>{
//no need to use byname, bycategory etc. Just filter the based on fields that have a value. To remove a filter just set its value to ''.

let filtered = data.filter(e => district ? e.district === district:true ) //return all data if no filter by district

filetered = filtered.filter(e => {
    if(!name) return true;
    if(e[status] === name) return true;
    return checkInStatus(e,name,'name')
   })
},[name,category,district,status])


Only some filters are explained. You can use the same logic for the rest of the filters also

For status no need to filter the data . While rendering just render only the fields for that status.

filteredData.map(e=> e[status].map(c=>c.name)}) // store status as a state
like image 44
user9747 Avatar answered Nov 15 '22 06:11

user9747