简体   繁体   中英

C# using Where (Linq) to filter a list with four condition

I have a list got it from the database (using dataset):

IEnumerable<DataSet.spGetDataRow> MyList = new DataSetTableAdapters.spGetDataTableAdapter().GetData(Date1, Date2, PID, RID).ToList();    

and I have 4 checkbox

<asp:CheckBox ID="CheckBox1" Text="1" runat="server" />
<asp:CheckBox ID="CheckBox2" Text="2" runat="server" />
<asp:CheckBox ID="CheckBox3" Text="3" runat="server" />
<asp:CheckBox ID="CheckBox4" Text="4" runat="server" />

what I need is to filter 'MyList'

I did the following:

if (CheckBox1.Checked && !CheckBox2.Checked && !CheckBox3.Checked && !CheckBox4.Checked)
{
    MyList = MyList.Where(a => a.Avg < 2);
}
else if (CheckBox2.Checked && !CheckBox1.Checked && !CheckBox3.Checked && !CheckBox4.Checked)
{
    MyList = MyList.Where(a => a.Avg >= 2 && a.Avg < 3);
}
else if (CheckBox3.Checked && !CheckBox2.Checked && !CheckBox1.Checked && !CheckBox4.Checked)
{
    MyList = MyList.Where(a => a.Avg >= 3 && a.Avg < 6);
}
else if (CheckBox4.Checked && !CheckBox2.Checked && !CheckBox3.Checked && !CheckBox1.Checked)
{
    MyList = MyList.Where(a => a.Avg >= 6);
}

this work great if I need to check only one checkbox at a time, but what if I want to check 2 or 3 at a time.

creating so many if conditions is not the best solution, I need to do it through linq if posible

what I was doing is to create a new MyList, and 'concat' the filtered one into it, but it didn't end well.

Thank you in advance.

Do it vice versa. Test each checkbox. If it is not checked remove the entries which are represented by this checkbox. Or better take those which are not represented. So if CheckBox1 is not checked take those entries which are not <2 that means they are >=2.

    if (!CheckBox1.Checked)
    {
        MyList = MyList.Where(a => a.Avg >= 2);
    }
    if (!CheckBox2.Checked)
    {
        MyList = MyList.Where(a => a.Avg < 2 || a.Avg >= 3);
    }
    if (!CheckBox3.Checked)
    {
        MyList = MyList.Where(a => a.Avg < 3 || a.Avg >= 6);
    }
    if (!CheckBox4.Checked)
    {
        MyList = MyList.Where(a => a.Avg < 6);
    }

In a next step you should precalculate the average values so this function isn't called so many times. Eg build tuples (row, average) then filter the average values and get back the rows by extracting them from the tuples.

Your question intrigues me to find a solution using a Dictionary. This will leave your code to a simple two lines like these (well also a simple function is required)

int index = CreateIndexFromCheckboxSelected();
var result = MyList.Where(x => dict[index].Invoke(x));

To reach this solution you need to build a Dictionary with a numeric key and a Func as value.

The numeric key is a byte sum of the various checkbox respecting a boolean logic in which the checkBox1 represents the bit at position 1, checkbox2 the bit at position 2 and so on....

The Func part is the lambda expression required by the where clause applied to your list of sqGetDataRow elements

You need 16 entries in this dictionary, one for each of the possible combination of the 4 checkboxes, each entry contains the lambda expression to be used in the where clause to extract the required spGetDataRow element.

Dictionary<int, Func<spGetDataRow,bool>> dict = new Dictionary<int, Func<spGetDataRow, bool>>()
{
    {0, (spGetDataRow a) => true},                      // No checkboxes checked return all
    {1, (spGetDataRow a) => a.Avg < 2},                 // Only checkbox1 checked
    {2, (spGetDataRow a) => a.Avg >= 2 && a.Avg < 3},   // Only checkbox2 checked
    {3, (spGetDataRow a) => a.Avg ????},                // CheckBox1 AND Checkbox2 checked
    {4, (spGetDataRow a) => a.Avg >= 3 && a.Avg < 6},   // CheckBox3 checked
    {5, (spGetDataRow a) => a.Avg ????},                // CheckBox3 + ChecBox1 checked
    {6, (spGetDataRow a) =>  a.Avg >= 2 && a.Avg < 6},  // CheckBox3 + CheckBox2 checked
    {7, (spGetDataRow a) => a.Avg ????},                // CheckBox3 + CheckBox2 + CheckBox1 checked    
    {8, (spGetDataRow a) => a.Avg >= 6},                // CheckBox4 checked
    {9, (spGetDataRow a) => a.Avg ????},                // CheckBox4 + CheckBox1 checked
    {10, (spGetDataRow a) => a.Avg ????},               // CheckBox4 + CheckBox2 checked
    {11, (spGetDataRow a) => a.Avg ????},               // CheckBox4 + CheckBox2 + CheckBox1 checked    
    {12, (spGetDataRow a) => a.Avg ????},               // CheckBox4 + CheckBox3 checked
    {13, (spGetDataRow a) => a.Avg ????},               // CheckBox4 + CheckBox3 + CheckBox1 checked
    {14, (spGetDataRow a) => a.Avg ????},               // CheckBox4 + CheckBox3 + CheckBox2 checked
    {15, (spGetDataRow a) => true}                      // All checkboxes selected return all
};

The example above contains the correct lambda only for the info I can extract from your question, but as you can see it is just a matter to replace the placeholder lambda with the correct one that you require for the various combinations of checkboxes.

To complete the example there is just one piece missing, but this is pretty trivial

public int CreateIndexFromCheckboxSelected()
{
    int chk1 = (CheckBox1.Checked ? 1 : 0);
    int chk2 = (CheckBox2.Checked ? 2 : 0);
    int chk3 = (CheckBox3.Checked ? 4 : 0);
    int chk4 = (CheckBox4.Checked ? 8 : 0);
    return (chk1 + chk2 + chk3 + chk4);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM