3

Is there a shorter way to check my random number from 1 - 100 (catNum) against this table of animals? This one doesn't look so bad but I have several more larger tables to work through, I would like to use less lines than I would have to using the statement below:

if (catNum < 36) { category = "Urban"; }
        else if (catNum < 51) { category = "Rural"; }
        else if (catNum < 76) { category = "Wild"; }
        else if (catNum < 86) { category = "Wild Birds"; }
        else { category = "Zoo"; }

Example of further tables:

example of larger tables

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • Elegance is in the eye of the beholder, and therefore opinion based, which is something Stack Overflow doesn't do. If you can provide an objective metric by which answers can be judged (length, number of operations, speed, etc.), then perhaps this question could pass muster. See [ask] and [help/dont-ask]. – Heretic Monkey Mar 07 '18 at 01:05
  • ok cool i'm a noob to this site - with the larger tables i am looking to go through them in less lines. should I edit my question? – gamblePants Mar 07 '18 at 01:08
  • If that's your criteria, sure [edit] away. – Heretic Monkey Mar 07 '18 at 01:10
  • hopefully that makes more sense now – gamblePants Mar 07 '18 at 01:16
  • @gamblePants your code seems reasonable. Only thing I’d suggest is not putting things in line. I don’t know if you did that for brevity here but your production code should not be all on one line – maccettura Mar 07 '18 at 01:19
  • thanks for the quick feedback @maccettura - appreciate it, and suggestion noted. – gamblePants Mar 07 '18 at 01:20
  • In this particular case, combining the else if with the single statement in braces into a single line is OK. – nicomp Mar 07 '18 at 01:20
  • I'm not sure that there's an *shorter* way; at some point there needs to be an association between the number and the category. You either do that with `if`s, or you do it with a hashmap (e.g., `{"01-35":"Urban", "36-50":"Rural"}`). The hashmap would reduce the amount of code in the actual functions that parse the categories, but those associations still need to be stored somewhere. – Heretic Monkey Mar 07 '18 at 01:24
  • @nicomp no. It’s never ok to do that. You’re code is not quicker with less new lines, make your code readable and uniform. Future, more enlightened you will thank you – maccettura Mar 07 '18 at 01:24
  • @maccettura It's completely OK to do that. – nicomp Mar 07 '18 at 01:32
  • @MikeMcCaughan thanks - i will give that a go – gamblePants Mar 07 '18 at 01:36
  • I must ask, is there an actual reason for the numbers? This looks like a translation from a table top game, played with dice. When programming there is no need for this. If you need a random animal, just select a random animal... – Drunken Code Monkey Mar 07 '18 at 01:41
  • @gamblePants check my answer below. It is a new feature available with C# 7 version. You can further modify it. Just check in detail, it is very interesting and also proving many more condition with switch case. According to me, it is good suitable for you. – Gaurang Dave Mar 07 '18 at 01:44
  • thanks to everyone who helped me out with this. i found almost all of the answers useful in some way. in the end i used a combination of using a hashmap (dictionary ) and putting it in another class with a get method. now that i see my question is a duplicate i'll check that thread out too – gamblePants Mar 13 '18 at 06:23

4 Answers4

5

I prefer to use something like this instead of many if/else A category class

class Category
{
    public int Min { get; set; }
    public int Max { get; set; }
    public string Name { get; set; }
}  

Initialise categories once and fill it with your values

var categories = new List<Category>();

and finally a method to resolve the category

public static string Get(int currentValue)
{
    var last = categories.Last(m => m.Min < currentValue);
    //if the list is ordered 
    //or
    // var last = categories.FirstOrDefault(m => m.Min <= currentValue && m.Max >= currentValue);
    return last?.Name;
}
maccettura
  • 10,514
  • 3
  • 28
  • 35
Khatibzadeh
  • 460
  • 3
  • 10
  • I am not sure what you mean, but u can send the list as second parameter to method and do the filtering on that – Khatibzadeh Mar 08 '18 at 03:57
  • oops sorry i deleted that first comment haha, i was going to put up a link to my code. i was wondering trying it that way, i'll give it a go thanks – gamblePants Mar 08 '18 at 04:19
2

One alternative is to build up a full list of the items, then you can just select one, at random, by index:

var categories =
    Enumerable.Repeat("Urban", 35)
        .Concat(Enumerable.Repeat("Rural", 15))
        .Concat(Enumerable.Repeat("Wild", 25))
        .Concat(Enumerable.Repeat("Wild Birds", 10))
        .Concat(Enumerable.Repeat("Zoo", 15))
        .ToArray();

var category = categories[45]; //Rural;
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
1

Yes, this is a well-studied problem and there are solutions that are more efficient than the if-else chain that you've already discovered. See https://en.wikipedia.org/wiki/Alias_method for the details.

My advice is: construct a generic interface type which represents the probability monad -- say, IDistribution<T>. Then write a discrete distribution implementation that uses the alias method. Encapsulate the mechanism work into the distribution class, and then at the use site, you just have a constructor that lets you make the distribution, and a T Sample() method that gives you an element of the distribution.

I notice that in your example you might have a Bayesian probability, ie, P(Dog | Urban). A probability monad is the ideal mechanism to represent these things because we reformulate P(A|B) as Func<B, IDistribution<A>> So what have we got? We've got a IDistribution<Location>, we've got a function from Location to IDistribution<Animal>, and we then recognize that we put them together via the bind operation on the probability monad. Which means that in C# we can use LINQ. SelectMany is the bind operation on sequences, but it can also be used as the bind operation on any monad!

Now, given that, an exercise: What is the conditioned probability operation in LINQ?


Remember the goal is to make the code at the call site look like the operation being performed. If you are logically sampling from a discrete distribution, then have an object that represents discrete distributions and sample from it.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
1
string[] animals = new string[] { "Urban", "Rural", "Wild", "Wild Birds", "Zoo" };
int[] table = new int[] { 35, 50, 75, 85 };     
for (int catNum = 10; catNum <= 100; catNum += 10)
{
    int index = Array.BinarySearch(table, catNum);
    if (index < 0) index = ~index;
    Console.WriteLine(catNum + ": " + animals[index]);
}

Run online: https://dotnetfiddle.net/yMeSPB

skyoxZ
  • 362
  • 1
  • 3
  • 10