Persistent Lists

Have you ever seen this error?

error

Have you ever seen this error and wanted to throw something at your computer because you know for a fact you have an empty list that does NOT have an *empty-string*? Yet every time you try to add something to your list, you get that nasty error. If you are a victim of *empty-string*, then this tutorial is for you.

I actually have a hidden agenda with this tutorial and that is to preface a series I will do on building a voting app that does not utilize the voting component. More on that in the next chapter. I personally have faced this problem many times and it wasn’t until I made my last app that I finally figured out what causes it, and how to fix it. The app I made relies heavily on lists and indexing to match list items, so as you can imagine this error is a killer for something of that nature. I am very happy that I am able to share this so that anyone facing this problem will now be able to resolve it without to much trouble.

As you are probably well aware, computers are very touchy and they are not at the point yet where computers can figure out what we want and adjust without human input. And so, this leaves us with situations where we know we gave the computer the correct instructions, yet it refuses to abide by them. In retrospect, the computer is actually doing exactly what you told it, which is why we end up with results such as the one I’m discussing.

Lets build a simple app, where we enter text into 2 fields, which are both stored using twdb, then retrieved during the screen initialize event, and finally loaded into our list variables. We also have a button, when pressed will list out all our previous entries with the fields paired. So think of the two fields as the subject and details.

The UI is very simple.

list3

  • VerticalArrangement – named Page1, width/height both set to Fill parent
    • HorizontalArrangement – width set to Fill parent and Height set at 145 pixels
      • VerticalArrangement
        • VerticalArrangement – Height set at 95 pixels
        • Button – Named SubmitBtn
      • VerticalArrangement – Width and Height both set to Fill parent
        • TextBox – named SubjectBox – Width set to Fill parent
        • TextBox – named DetailsBox – Width and Height both set to Fill parent
      • Button – named ListBtn and width set to Fill parent
  • VerticalArrangement – named Page2 and width set to Fill parent
    • Label – named DisplayLbl and width set to Fill parent
  • Notifier component
  • TinyWebDB component

The page arrangements do not come into play yet, but as I get into later tutorials where I discuss creation of the voting app, these will be utilized. And I haven’t forgotten the primary purpose of this tutorial, to show how to fix the *empty-string* problem. Keep reading, the solution is coming. Just to let you know, I am going to purposely set this up incorrectly so that the results produce the error, and finally I will show the adjustment needed to resolve it.

Ok, now that we have our UI setup, we can proceed to the blocks editor.

list4

The first thing we want to do is declare some variables for our lists. This is nothing you haven’t seen before. We have a variable for our subject and one for our detail and each one contains the make a list block.

We then built out the action for our submit button click event. I gave it a little extra flair using an ifelse test to make sure that you enter something in both fields before you are able to submit. You can see that in the test socket, I use an “or” to test if either the subject or detail textbox has an empty string. If it does, the notifier will prompt the user and exit,  performing not further actions.

When both textboxes have something entered the else-do actions will run and store the value in the subject and detail textboxes to their respective list variables, then call the twdb storevalue to save the data to the db, and at the end the subject and detail textboxes will be cleared.

At this point we can enter data into the app, but that’s about it. If we restart the app, the data will not be available and we also don’t have a way to view it yet. Lets start by adding the function to view what we already entered, which I do by creating a procedure.

list5

I renamed the procedure to “listView” and I declared a variable to use as an index for my foreach loop that will loop through the lists and show me everything entered, while matching the subject to the details.

The procedure contains to tests, with a foreach nested inside the ifelse. The reason I do this is because I want to return a true or false which I can use to provide some information to the user. The first test checks to see if the global subject list is a list. Does it contain any data? If it does not, a false is returned and else-do will call the notifier which will tell the user whatever I want, and I have chosen to tell them that nothing has been entered yet.

If the initial test returns a true, then-do will be called and the displaylbl.text will receive the results of the procedure to list out my list contents. The first thing that will happen is the make text call will create a heading that looks like this:

ITEMS
——————————-

Following that the subjectIndex will be initialized with 1 which corresponds to the first item in the list and the foreach loop will execute. Every item that is passed to the foreach loop will be given the variable name subject, which will contain the value of a each entry for the duration of it’s life in the loop. This is not a global variable so I can only access it within the loop I initialize it in. As foreach works it will set the DisplayLbl.Text using another make text call which will start by setting the .text to DisplayLbl.Text. What this does is take what is already in that label and list it again. What this does is retain what was listed already, append the next item and start over. You can see that by following the make text call down. The next item is a new line character, then my value subject which holds a value from the global subject list I plugged into the “in list” socket at the bottom of foreach. Then I have about 20 empty spaces followed by the index check to make sure that the detail I list matches the subject it was saved with. This is done by calling select list item from the global detail and identifying which item in global detail to use by calling position in list for the index socket. The position is identified by saying, my value subject should return a position value in the list global subject. This value is passed back to the select list item index, which in turn is passed to the global detail, which takes the position value and returns the item stored at this position. Did I say the word position enough times? This ends with the subjectIndex incrementing +1 and the loop starts over at index location 2 and continues doing this until the end of the list is reached.

Here is a diagram to help visualize how this indexing process works.

List1                   List2
Text1                 Text1a
Text2                 Text2a
Text3                 Text3a
Text4                 Text4a

Lets use Text3 as the example. Foreach is executing and “Text3” is assigned to value subject and it is written to the label.text. Which now looks like this

ITEMS
——————————-
Text1                 Text1a
Text2                 Text2a
Text3

The 20 spaces is added after it and select list item is called. It says, get me an item from global detail, but not any item. I want the item that is in position number…and the position number you should get from the global subject with the current value subject. If you have ever looked at the raw data in the db, it looks something like this.

List1 | (Text1,Text2,Text3,Text4)
List2 | (Text1a,Text2a,Text3a,Text4a)

If you start from left to right and write numbers above each one starting with 1 it will look like this. Note, this is not actually shown in the db, it is inherent with the indexing of each value.

1           2           3           4
List1 | (Text1,Text2,Text3,Text4)
List2 | (Text1a,Text2a,Text3a,Text4a)

And so, foreach said, give me a value from global detail (list2), but I want the position that is equal to the position that value subject holds in global subject (list1). In the above example we had already reached Text3 so the lookup to the db for an index value would see that value subject is Text3 and it’s position in list is “3”. It will take that 3 and look at list2 and see that list 2 at position 3 is Text3a, and complete that row.

ITEMS
——————————-
Text1                 Text1a
Text2                 Text2a
Text3                 Text3a

And so on,  until the entire list has been written to the label.text.

Now we have a function to build our lists, we have a function to list out the entries in our list using ListBtn.Click to call the listView procedure, so all we need now is to make sure our persistent values are loaded into their respective lists every time the app is launched.

list1

To accomplish this task we use the Screen1.Initialize event to call the twdb.GetValue with a tag. We do this twice, once for the tag we used to save the subject list and once for the detail list.

Once we have the value, we use twdb.GotValue to place the contents in their respective lists. We use two if tests. The order doesn’t matter. I first test to see if the value tagFromWebDB equals the tag I stored the subject under (subjectE). If it does, I set the global subject to the valueFromWebDB. Once this completes, both of my lists will contain whatever is stored in the respective tags on twdb.

So that’s it. It looks to me like I now have a completed app that can store data, retrieve the stored data, and list it out with proper relationships. I decide to test my app and I restart it with a clean slate. The app starts, I press submit and I get an alert that something needs to be entered before I can submit. Great, this part works. Next I press List and it shows me the heading of my list, but nothing following. Great, this works. Now to enter some data. I enter something for subject, I enter something for detail and I hit submit, only to be hit with.

error

Why? I have not entered anything and my app does not allow me to submit an empty value. What is going on? Well, the problem lies in what we told our app to do when the screen initializes. We said, get the values from db and load them into our lists. Well, the first time you do this there are no values to load so it will still do exactly what you told it to do and load the only value it can, which is an empty string.

The fix is very simple and only requires making a small modification to the twdb.GotValue blocks.

list2

Notice, these blocks are still the same for the most part, with the inclusion of the if test for each value. The test does the following. It checks if the attached variables (global subject & global detail) are lists by calling “is a list?”. If it is, meaning there is data there, the data will be loaded to our list variables for use by the app. If there is nothing there, it will simply exit and not load anything.

Restart the app again, enter the subject and detail, click submit and smile.

Just remember, when dealing with computers your instructions will be carried out explicitly, which in many cases will result in bugs. Always be sure to validate your data before using it.

You may also like...

  • Pingback: TinyWebDB confusion! PLEASE HELP! - Android Forums()

  • Pingback: Trouble with tinyDB - Android Forums()

  • Cssg

    Awesome, thanks!!! 😀

  • Hubbleas

    GRACIASSSSSSSSSSSS

  • Mike-mazza

    I have followed your tutorial but I am having a problem.  If I enter an item1 (saved in a itemList) and, say money1 (saved in a moneyList), it displays properly,  If I enter item2 and money2, it displays properly.  If I enter item3 and money3, still ok.  HOWEVER, if I re-enter item1 and a different money amount, it displays item1 with original money1 amount instead of the new money amount.  Any suggestions?  It looks like a sequencing problem but I am a little confused.  Please respond to mike-mazza@hotmail.com.  Thanks for your help.

  • Mike-mazza

    Never mind.  Got it.  Thanks!!

  • Mike-mazza

    Sorry to bother again so soon.  Thanks for the previous info Gene.  Here is my next dilemma.  Any suggestions as to the best way to do this would be appreciated.

    So now I have 2 lists, one is itemList, the other is moneyList.  Every time the user enters an item (stored in the itemList) the user is also required to enter the dollar amount (stored in the moneyList).  Both are stored together, with the same index number.  ie: itemList entry #1 is in position number 1 in the itemList and it’s associated dollar amount is in position #1 in the moneyList,  (I could also store those values together in one list if someone thinks it easier to manipulate)  A user may enter different money amounts for the same item.  see below

    index          itemList     moneyList

    1                  item1          20.00
    2                  item2          10.00
    3                  item3          15.00
    4                  item1          12.00
    5                  item2          33.00

    So my question is this.  (actually 2 questions)

    If a user decides to remove all occurrences of item1 and it’s associated dollar amount, what would be the best way to do this?

    Also, how would one go about searching these list for say, all occurrences of item 2 and total up the dollar amounts?

    Thanks for any suggestions….. keeping my mind working by still learning at 58…..

    • It’s been a while since I’ve done this stuff with AI, but I’ll give it a shot.

      1) I believe AI has a function to delete an entry. It’s setup using a listpicker. Then you tell it to remove the item selected from the listpicker.

      2) I think you can use a contains block to search for whatever the input is, and you can probably grab the results and sum them. It may require additional work to parse the results, but it’s probably doable. Maybe try checking other tutorial sites (check http://www.tair.info) and searching google to find examples.

      I hope this helps. 🙂

  • Roman Romero

    Hi, Gene, I can’t see the images, please reload, thx and regards