[SOLVED] Is there any easy way to find if a number exists in a list?

Here's a copy/paste from my sketch:

if 
     (((men == 7) && (p > 1) && (p < 8)) ||
      ((men == 8) && (p > 1) && (p < 4)) ||
      ((men == 9) && (p > 1) && (p < 4)) ||
      ((men == 10) && (p == 2)) ||
      ((men == 11) && (((p > 1) && (p < 5)) || (p == 7))))
  {
      // stuff to do if "p" was found
  }

[EDIT NOTE]
OK, post #3 below suggested I add "inline documentation to the above to explain what it is doing, so here it is:

if 
           // If menu 7 is open, are we hovering over item 2, 3, 4, 5, 6 or 7?
     (((men == 7) && (p > 1) && (p < 8)) ||
           // If menu 8 is open, are we hovering over item 2 or 3?
      ((men == 8) && (p > 1) && (p < 4)) ||
           // If menu 9 is open, are we hovering over item 2 or 3?
      ((men == 9) && (p > 1) && (p < 4)) ||
           // If menu 10 is open, are we hovering over item 2?
      ((men == 10) && (p == 2)) ||
           // If menu 11 is open, are we hovering over item 2, 3, 4, or 7?
      ((men == 11) && (((p > 1) && (p < 5)) || (p == 7))))
  {
      // stuff to do if "p" was found
  }

[END EDIT]

The above is both hard to look at, and time-consuming to place all the () brackets correctly.

Is there an easy way to accomplish the same thing?, something like:

void hasNumber(byte n)
{
  if (n in [1,7,14,23,32,64,100])
    return true;
  else
    return false;
}

"in [...]" isn't being recognized as valid code.

boolean hasNumber(byte candidate)
{
  boolean foundStatus = false;
  const byte testNumbers[] = {1,7,14,23,32,64,100};
  for (i = 0; i<7 and foundStatus==false; i++)
    {
    if  (candidate == testNumbers[i]) foundStatus = true;
    }
  return foundStatus;
}

Thanks for the example. I see how that would work.

But if I applied that to the copy/paste example I gave above, I think the code would become even MORE complicated, not less.

Interesting approach, though.

I didn't try to understand your sketch snippet. I just answered the question as it pertained to your second proposal.

The approach is not mine, although I would love to take credit for it. It is a common textbook problem in programming.

Your code is very confusing and opaque. I would not like to have to maintain it. Although in a small context it may work, such run-on complexity is a sure sign that your high level design is bad.

Edit - especially with no inline documentation.

I see that you already realize this. But without telling us what it is supposed to do, we can not help you simplify it.

Aarg's solution is pretty simple. I modified it a little, but it's pretty easy to understand.

void setup() {
  Serial.begin(9600);
}

void loop() {
  char inputStr[5];
  int charsRead;
  int searchFor;
  int retVal;

  if (Serial.available() > 0) {
    // Get all input up to newline or size of buffer minus 1
    charsRead = Serial.readBytesUntil('\n', inputStr, sizeof(inputStr) - 1);  
    
    inputStr[charsRead] = '\0';     // Make a C string
    searchFor = atoi(inputStr);     // Make it an int 
    Serial.print("Looking for match on: ");
    Serial.println(searchFor);
    
    retVal = hasNumber(searchFor);  // In the set??
    if (retVal < 0) {
      Serial.println("No match");
    } else {
      Serial.print("Matched at position: ");
      Serial.println(retVal);
    }
  }

}

/*****
   Purpose: To see if a given value is in an array of test numbers

   Parameter list:
      int candidate          what we are looking for

   Return value:
      int                         -1 on no match, the position in the array on match
*****/

int hasNumber(int candidate)
{
  int i;
  const int testNumbers[] = {1, 7, 14, 23, 32, 64, 100};
 
  for (i = 0; i < sizeof(testNumbers) / sizeof(testNumbers[0]); i++)
  {
    if  (candidate == testNumbers[i]) 
      return i;               // Could determine the position if useful
  }
  return -1;
}

While I can be dinged on multiple return points in the function, it's so simple I'll live with it.

That just might work for me, making it easier to see and to update: I could then rewite my original expression something like:

void myFunction(ActiveItem)
{
    byte items[9];
    switch (men)
    {
        case  7: items = {2 ,3 4, 5, 6, 7}; break;
        case  8: case 9: items = {2, 3}; break;
        case 10: items = {2}; break;
        case 11: items = {2, 3, 4, 7}; break;
    }
    return hasNumber(ActiveItem);
}

This isn't quite right though. The line:
case 7: items = {2 ,3 4, 5, 6, 7};
reports: "expected primary-expression before '{' token"

PS-- I added a second example to my original post, including the "inline documentation" that arrg asked about in comment #3. And just now added the breaks comment #6 pointed out I forgot to put in my case statement.

to make hasNumber more generic one could pass the array and its size as parameter

int hasNumber(int candidate, int testNumbers[], int count)
{
  int i = count;
  while (--i >= 0)
  {
    if  (candidate == testNumbers[i]) break;
  }
  return i;
}

if the testNumbers array is large (>25 or so) binary search might speed things up if needed

@CosmicGold
you need at least a few breaks in the switch

inspired by CosmicGold - what about using bitmasks?

int inMenu(int men, int p)
{
  p = (1 << p);
  switch(men)
  {
    case 7: return p & 0xFC;
    case 8: return p & 0x0C; 
    case 9: return p & 0x0C; 
    case 10: return p & 0xFC;
    case 11: return p & 0x9C;
  }
  return 0;
}

update:
it could even be an array of bytes and one formula,
however relation with original problem is a bit lost.

int inMenu(const int men, const int p)
{
  byte masks[] = { 0,0,0,0,0,0,0,0xFC, 0x0C,0x0C, 0xFC, 0x9C, };
  return masks[men] & (1<<p);
}

I think your "bitmask" idea is a great one. That can make things fast and simple. It so happens that my LCD has room for a max of 8 items per menu (exactly one byte of bits). :wink:

But still, do you see the reason why my code in comment #5 above reports:
"expected primary-expression before '{' token" ?

CosmickGold:
I think your "bitmask" idea is a great one. That can make things fast and simple. It so happens that my LCD has room for a max of 8 items per menu (exactly one byte of bits). :wink:

But still, do you see the reason why my code in comment #5 above reports:
"expected primary-expression before '{' token" ?

arrays can only be assigned with {...} when declared, not after that.
IIRC the compiler interprets the { as a "scope begin" operator.

the "mem =" statement therefore misses an expression on the right site

robtillaart:
arrays can only be assigned with {...} when declared, not after that.
IIRC the compiler interprets the { as a "scope begin" operator.

the "mem =" statement therefore misses an expression on the right site

Thank you for explaining that. Knowing why helps a lot.


We now have four ways to do this thing, all of which would work.

(1) My original way, which is difficult to read or update without bracket confusion.
(2) The standard way, first posted by aarg, which is too long on code for my needs.
(3) The bitwise way, posted by robtillaart, which would be super-fast for loops, almost like machine language; but has the obvious down side of making it so hard to see what's happening; like, exactly which menu items does "0xFC" include? I'd use it if speed was essential; but fortunately, I only need to call is function once when the user selects an item.
(4) My new (and final) way, using the String class (which many say to avoid because the String class is such a "memory hog"). Also, my using the String class is likely the slowest way possible, with so many conversions and reiterations. But for me it's the best because it's brief on code, easiest to see, understand, and update.

So here is method (4), processing exactly the same selection as my first example at the top:

boolean inStr(byte n, String s)
{
    s = "," + s + ",";
    String s2 = "," + String(n) + ",";
    return  s.indexOf(s2) > -1;
}

void testP(byte p)
{
    byte b = false;
    switch (men)
    {
          case 7:  b = inStr(p,"2,3,4,5,6,7");
          case 8:  b = inStr(p,"2,3,5,6");
          case 9:  b = inStr(p,"2,3,4");
          case 10: b = inStr(p,"2");
          case 11: b = inStr(p,"2,3,4,7");
    }
    if (b)
    {
         // stuff to do if "p" was found
    }
}