5

I have litle problem. I'm a new dev and I have view with "Age criterion" for my product.

These options are: "< 24", "24 - 35", "35 - 45", "> 45".

A json which I need to work looks that:

"age": {
        "lessThan24": true,
        "between24And35": true,
        "between35And45": true,
        "moreThan45": true
        }

My View model:

public class AgeCriterionViewModel
{
    public bool? LessThan24 { get; set; }
    public bool? Between24And35 { get; set; }
    public bool? Between35And45 { get; set; }
    public bool? MoreThan45 { get; set; }
}

And my domain model:

public class AgeCriterion
{
    public int? From { get; set; }
    public int? To { get; set; }
}

So here is my problem. I need to map viewmodel to domain model. But these booleans are nullable so they can be null, false or true. OLSO it can be multiselect. So these options may be "lessthan24" and "between35and45" or all of them or none.

I thinking about build some terrible BIG IF construction. But how can i check all options? Is there any nice way?

I have that at this moment:

       if (age == null)
            return null;
        AgeCriterion criterion = new AgeCriterion();

        if (age.LessThan24.HasValue)
        {
            if (age.LessThan24.Value)
            {
                criterion.From = 0;
                criterion.To = 24;
            }
        }
Nerf
  • 938
  • 1
  • 13
  • 30
  • Separate it out, create methods that will convert a nullable boolean into a boolean as a standard and reuse. As for a nice way, not that I'm aware of, you'll have to do if statements somewhere, but make it so the code is reusable to save yourself in the future – Draken May 27 '16 at 11:29
  • 3
    How do you manage criterion if I check `LessThan24` & `Between24And35` ? – Thomas Ayoub May 27 '16 at 11:30
  • `public boolean isValid(boolean? value){ return value.hasValue ? value : false; }` As an example for your method. Then you can just do `if (isValid(age.LessThen24)){ //Your code }` – Draken May 27 '16 at 11:31
  • 2
    You could create enum and use bit mask like `var result = AgeCriterion.BelowTwentyFive | AgeCriterion.ThirtyToFourtyFive;`, check this : http://stackoverflow.com/questions/1030090/how-do-you-pass-multiple-enum-values-in-c – Fabjan May 27 '16 at 11:40
  • 1
    @ThomasAyoub In that case I would assume it would be 0 to 35. A better question is what if the only selected values are `LessThan24` and `MoreThan45`? In that case it seems like you'd need multiple `AgeCriterion` to represent the model. – juharr May 27 '16 at 11:51
  • 2
    @juharr Exactly. Single `AgeCriterion` cannot represent all possible combinations, which we assume are combined with `or`. Also I would assume `null` means `do not apply`, but it's unclear how the `false` value should be treated? For instance, `LessThan24 == false` could be treated as `>= 24`. – Ivan Stoev May 27 '16 at 12:00
  • "A better question is what if the only selected values are LessThan24 and MoreThan45?" @juharr then AgeCriterion From will be 0 and To will be 100 or 200. – Nerf May 27 '16 at 12:28
  • @Nerf I don't know exactly what you are using this for, but if I selected those two I would not expect it to include 24 to 45 as well. – juharr May 27 '16 at 12:43
  • So it's better to use few options than one? How can I remodel that? To multiple AgeCriterion or collection of ints? – Nerf May 27 '16 at 12:45

2 Answers2

4

You could create enum and use bitmask :

[Flags]
public enum AgeCriterion
{
   NotSpecified = 0,
   BelowTwentyFour = 1,
   TwentyToThirtyFive = 2,
   ThirtyToFourtyFive = 4,
   MoreThanFourtyFive = 8,
}

public class AgeCriterion
{
    public AgeCriterion Age { get; set; }
}

And map viewmodel to this enum like this :

  var criterion = new AgeCriterion();

  if (age == null)
        return criterion; // fill free to return null if it better suit your needs

  AgeCriterion age;

  age  = age.LessThan24 == true ? age | AgeCriterion.BelowTwentyFour : age;

  age  = age.Between24And35 == true ? age | AgeCriterion.TwentyToThirtyFive : age;

  age  = age.Between35And45 == true ? age | AgeCriterion.ThirtyToFourtyFive : age;

  age  = age.MoreThan45 == true ? age | AgeCriterion.MoreThanFourtyFive : age;

  criterion.Age = age;

  return criterion;

P.S. To check for specific criterion(s) in code you can do this :


To do something for people from 24 to 35 and over 45 years old (i wouldn't say it makes much sense to me, but still)

if(criterion.Age == AgeCriterion.TwentyToThirtyFive &  criterion.Age == AgeCriterion.MoreThanFourtyFive)

To do something for people from 24 to 35 or over 45 years old


if(criterion.Age == AgeCriterion.TwentyToThirtyFive |  criterion.Age == AgeCriterion.MoreThanFourtyFive)
Fabjan
  • 13,506
  • 4
  • 25
  • 52
  • It look very clear but it is too complicated to me and I dont understand what is going on here :( And I need to specify range/scope FROM, TO. – Nerf May 27 '16 at 12:30
  • You do specify ranges with this method, you can find more basic example here : http://stackoverflow.com/questions/1030090/how-do-you-pass-multiple-enum-values-in-c or here http://www.dotnetperls.com/enum-flags – Fabjan May 27 '16 at 12:42
3

you can use the ?? Operator (C# Reference) to minimize your if/else.

if (age.LessThan24 ?? false)
{
    criterion.From = 0;
    criterion.To = 24;
}
else if (age.Between24And35 ?? false)
{
    criterion.From = 24;
    criterion.To = 35;
}
// And so on

what it does is:
if age.LessThan24 is null than use false, if not use the value of it:

if (age.LessThan24 != null)
{
    if(age.LessThan24.Value)
    {
        // ....

    }
}
Maximilian Ast
  • 3,369
  • 12
  • 36
  • 47
  • 2
    There is no need of special operators, `age.LessThan24 == true` will do the same, but that's not the main issue. The question is unclear and unresolvable in some cases. – Ivan Stoev May 27 '16 at 12:07