Author Topic: Demo: Getting arrays into an array of a UDT  (Read 4959 times)

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Demo: Getting arrays into an array of a UDT
« on: January 29, 2021, 08:15:38 pm »
Inspired by what I think @Dimster might be able to use for his recurring data problem.

It's nice trick even if it doesn't help him with that.

This demo records a hand written word (forget about dotting i's and crossing t's for moment) into an array of points and records the name of word and stores both into same UDT array item.

First part just collects words, 2nd part just plays them back in same order as entered, nothing fancy here:
Code: QB64: [Select]
  1. _TITLE "Getting an Array into UDT Array" 'b+ 2020-01-29
  2.  
  3. CONST Xmax = 1200, Ymax = 700, Comma = ","
  4. TYPE ArrayType
  5.     Aname AS STRING ' here is the name of the array
  6.     A AS STRING '    here is the array joined into a long string so it may be stored in a UDT
  7.  
  8. REDIM words(100) AS ArrayType ' record the script positions of handwritten words and save the word and positions into a UDT
  9.  
  10. SCREEN _NEWIMAGE(Xmax, Ymax, 32)
  11. _DELAY .25 'need time for screen to load before attempting to move it.
  12. DIM mx AS LONG, my AS LONG, mb AS LONG, w$, wi AS LONG, i AS LONG, j AS LONG
  13.  
  14. _TITLE "To gather data:  Hand write words with mouse and then enter the word, up to 100 words or escape."
  15. wi = 0
  16.     CLS
  17.     mb = _MOUSEBUTTON(1)
  18.     IF mb THEN
  19.         REDIM temp(0) AS LONG
  20.         mx = _MOUSEX: my = _MOUSEY
  21.         LongAppend temp(), mx
  22.         LongAppend temp(), my
  23.         PSET (mx, my)
  24.         WHILE mb
  25.             WHILE _MOUSEINPUT: WEND
  26.             mx = _MOUSEX: my = _MOUSEY: mb = _MOUSEBUTTON(1)
  27.             LongAppend temp(), mx
  28.             LongAppend temp(), my
  29.             LINE -(mx, my)
  30.             _LIMIT 10
  31.         WEND
  32.         CLS
  33.         INPUT "Enter word > "; w$
  34.         words(wi).Aname = w$
  35.         words(wi).A = Join$(temp(), Comma$)
  36.         wi = wi + 1
  37.         IF wi > 100 THEN wi = 100: EXIT DO
  38.     END IF
  39.  
  40. ' simple playback
  41. FOR j = 0 TO wi
  42.     CLS
  43.     PRINT "This is word "; words(j).Aname
  44.     REDIM unpackXY(0) AS LONG
  45.     Split2Long words(j).A, Comma$, unpackXY() ' the first data item in an LongAppend Array is at 1 not 0
  46.     FOR i = 2 TO UBOUND(unpackXY) STEP 2
  47.         IF i = 2 THEN PSET (unpackXY(i - 1), unpackXY(i)) ELSE LINE -(unpackXY(i - 1), unpackXY(i))
  48.         _LIMIT 20
  49.     NEXT
  50.     _DELAY 1
  51.  
  52. 'this splits the items separated by delim into an array of LONG type numbers
  53. SUB Split2Long (SplitMeString AS STRING, delim AS STRING, loadMeArray() AS LONG) 'modified for this app
  54.     DIM curpos AS LONG, arrpos AS LONG, LD AS LONG, dpos AS LONG 'fix use the Lbound the array already has
  55.     curpos = 1: arrpos = LBOUND(loadMeArray): LD = LEN(delim)
  56.     dpos = INSTR(curpos, SplitMeString, delim)
  57.     DO UNTIL dpos = 0
  58.         loadMeArray(arrpos) = VAL(MID$(SplitMeString, curpos, dpos - curpos))
  59.         arrpos = arrpos + 1
  60.         IF arrpos > UBOUND(loadMeArray) THEN REDIM _PRESERVE loadMeArray(LBOUND(loadMeArray) TO UBOUND(loadMeArray) + 1000) AS LONG
  61.         curpos = dpos + LD
  62.         dpos = INSTR(curpos, SplitMeString, delim)
  63.     LOOP
  64.     loadMeArray(arrpos) = VAL(MID$(SplitMeString, curpos))
  65.     REDIM _PRESERVE loadMeArray(LBOUND(loadMeArray) TO arrpos) AS LONG 'get the ubound correct
  66.  
  67. FUNCTION Join$ (numberArray() AS LONG, connect$) 'connect all the array itmes into long string separated by connect$ character
  68.     REDIM i AS LONG
  69.     FOR i = LBOUND(numberArray) TO UBOUND(numberarray)
  70.         IF Join$ = "" THEN Join$ = TS$(numberArray(i)) ELSE Join$ = Join$ + connect$ + TS$(numberArray(i))
  71.     NEXT
  72.  
  73. ' the first data item in an LongAppend Array is at 1 not 0 assuming a REDIM arr(0) '<< start with 1 item at index 0
  74. ' thusly the ubound of the array is the actual significant items it contains
  75. SUB LongAppend (arr() AS LONG, addItem AS LONG) ' grow the arr() as you add items to it
  76.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS LONG
  77.     arr(UBOUND(arr)) = addItem
  78.  
  79. FUNCTION TS$ (n AS LONG) ' just convert a long integer to string and remove front and end spaces
  80.     TS$ = _TRIM$(STR$(n))
  81.  
  82.  

Marked as best answer by bplus on January 30, 2021, 01:05:40 pm

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #1 on: January 30, 2021, 01:09:48 am »
That seems like an overly complicated way to store many pieces of data in a string. Leaving the Aname label aside for the moment, this is a much simpler way to do it:
Code: [Select]
DEFLNG A-Z

TYPE t
    s AS STRING
END TYPE
DIM v AS t

FOR i = 10 TO 0 STEP -1
    set v.s, i, i ^ 2
NEXT i

FOR i = 0 TO 10
    PRINT get(v.s, i)
NEXT i

SUB set (array$, index, value&)
    element_size = LEN(value)
    IF LEN(array$) < element_size * (index + 1) THEN
        array$ = array$ + STRING$(element_size * (index + 1) - LEN(array$), CHR$(0))
    END IF
    MID$(array$, index * element_size + 1) = _MK$(LONG, value)
END SUB

FUNCTION get& (array$, index)
    get = _CV(LONG, MID$(array$, index * LEN(get) + 1, LEN(get)))
END FUNCTION
(Change the &s and the 'LONG's to whatever data type you want).

You could rewrite this to use _MEM too, but I don't consider any speed gain to be worth the extra complexity of manual memory management, especially once you start building arrays of UDTs or passing the UDT around as an intermediate value.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #2 on: January 30, 2021, 09:51:14 am »
That seems like an overly complicated way to store many pieces of data in a string. Leaving the Aname label aside for the moment, this is a much simpler way to do it:
Code: [Select]
DEFLNG A-Z

TYPE t
    s AS STRING
END TYPE
DIM v AS t

FOR i = 10 TO 0 STEP -1
    set v.s, i, i ^ 2
NEXT i

FOR i = 0 TO 10
    PRINT get(v.s, i)
NEXT i

SUB set (array$, index, value&)
    element_size = LEN(value)
    IF LEN(array$) < element_size * (index + 1) THEN
        array$ = array$ + STRING$(element_size * (index + 1) - LEN(array$), CHR$(0))
    END IF
    MID$(array$, index * element_size + 1) = _MK$(LONG, value)
END SUB

FUNCTION get& (array$, index)
    get = _CV(LONG, MID$(array$, index * LEN(get) + 1, LEN(get)))
END FUNCTION
(Change the &s and the 'LONG's to whatever data type you want).

You could rewrite this to use _MEM too, but I don't consider any speed gain to be worth the extra complexity of manual memory management, especially once you start building arrays of UDTs or passing the UDT around as an intermediate value.

Fantastic, this is new, _CV and _MK$, to me. Apparently these control the size of each item according to it's type, so you don't need dividers to tell when one item ends and the next begins, cool!

Thanks @luke can't wait to try it out.
« Last Edit: January 30, 2021, 10:12:51 am by bplus »

Offline Dimster

  • Forum Resident
  • Posts: 500
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #3 on: January 30, 2021, 10:26:08 am »
My gawd you guys - two really clever programs.
@luke - that line of code
Quote
array$ = array$ + STRING$(element_size * (index + 1) - LEN(array$), CHR$(0))

It's taking the string array "v" and adding a string numeric value. I'm not sure what the value is but lets say its' 25. So the resultant new string is "v25" or would it be "v 25" with a space between the V and the 25?

Now the resultant array can only be used in that Subroutine (local array) and only used with string values. Is that correct?

@bplus . I see now what you mean by splitting up one long string name into the various names for the arrays I would like my program to develop on the fly. So, in the example I was using, if I forsee the user of the program could manipulate the various files of data into their Mean, Mode, Median, MovingMean, MovingMode, MovingMedian, with periods of 4, 10, 25 and knowing i have 50 files all with the same name but for the event file number then one massive array containing all that info could be used to form the exact array the user would request.

So
 Query$ = "MeanModeMedianMovingMeanMovingModeMovingMedian41025Event1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950"

Then invite the user to chose the Event and Math function to display the data, and using that info create a sub array from Query$ and the file name to access. I'll give it a go. It is dealing with string values ie Dim Shared MovingMedian4 (1 to 300) wouldn't work - has to be Dim Shared MovingMedian4$(1 to 300) and the data it's accessing has to be in quote marks.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #4 on: January 30, 2021, 10:43:14 am »
@Dimster

My Interpretation of this line:
Code: QB64: [Select]
  1. array$ = array$ + STRING$(element_size * (index + 1) - LEN(array$), CHR$(0))

If the new index item exceeds the array size then the above line fills in the difference with CHR$(0) characters probably translates to 0 values with _CV

Now with the array string the right size the next step is to substitute the value converted to ASCII number (base 256?)
plugged in at the index * type size with a MID$() substitution statement (not the MID$ function statement).

I wonder, what would happen for negative indexes?  Well not quite the versatility of an array? and speed comparison?

PS you can only do one type number for this method, because the ASCII conversion will remain the same size, that's the trick you need when using MID$ for putting and getting values, all the items have to be the same size in characters.
« Last Edit: January 30, 2021, 10:52:30 am by bplus »

Offline Dimster

  • Forum Resident
  • Posts: 500
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #5 on: January 30, 2021, 11:11:42 am »
I totally misunderstood that line of Luke's. Looked like a concatenation of string values to me rather than a resizing of the string array. 

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #6 on: January 30, 2021, 11:23:46 am »
Quote
@bplus . I see now what you mean by splitting up one long string name into the various names for the arrays I would like my program to develop on the fly. So, in the example I was using, if I forsee the user of the program could manipulate the various files of data into their Mean, Mode, Median, MovingMean, MovingMode, MovingMedian, with periods of 4, 10, 25 and knowing i have 50 files all with the same name but for the event file number then one massive array containing all that info could be used to form the exact array the user would request.

So
 Query$ = "MeanModeMedianMovingMeanMovingModeMovingMedian41025Event1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950"

Then invite the user to chose the Event and Math function to display the data, and using that info create a sub array from Query$ and the file name to access. I'll give it a go. It is dealing with string values ie Dim Shared MovingMedian4 (1 to 300) wouldn't work - has to be Dim Shared MovingMedian4$(1 to 300) and the data it's accessing has to be in quote marks.

LOL man, I am perplexed again!

@Dimster have you worked with TYPE structures? UDT's allow grouping of mixed data types, or just grouping things under one convenient structure could be all same type. Under one variable name you can have all sorts characteristics that make up the object or structure.

So like for data from a file you could have:
Type FileDataType
   Filename as string
   Data as string  ' our string array we are discussing in this thread
   Mean as single
   Average as single
   Highest as single
   Lowest as single
   Range as single
   STD as single
   ...
End Type

' now get ready to load and store from 50 files!
REDIM MyFiles(1 to 50) as FileDataType

' loading procedure on each file to load MyFiles with
open file
get array data string
get stats saved or calc now from array data string

' start queries of MyFiles
'like look at all of one kind of stat find something interesting and see what file it came from
« Last Edit: January 30, 2021, 11:24:58 am by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Demo: Getting arrays into an array of a UDT
« Reply #7 on: January 30, 2021, 11:36:33 am »
The way I tend to do arrays in types is like this:

Code: [Select]
TYPE a_type
    foo AS STRING * 104
END TYPE

DIM ex AS a_type
DIM array(100) AS LONG
DIM m(1) AS _MEM: m(1)= _MEM(array()): m(0) = _MEM(ex)

FOR i = 0 TO 100
    array(i) = i ‘do whatever with the array.
NEXT

_MEMCOPY m(1), m(1).OFFSET, m(1).SIZE TO m(0), m(0).OFFSET ‘copy the array to the proper position in the TYPE

‘do whatever with the UDT variable such as putting it to a file.

« Last Edit: January 30, 2021, 11:43:12 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #8 on: January 30, 2021, 11:39:55 am »
Seems like you can do away with the line(s)

Code: QB64: [Select]
  1. TYPE a_type
  2.     foo AS STRING * 104

As a_type isn't mentioned subsequently. Is this really arrays in types?
You're not done when it works, you're done when it's right.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Demo: Getting arrays into an array of a UDT
« Reply #9 on: January 30, 2021, 11:44:03 am »
Seems like you can do away with the line(s)

Code: QB64: [Select]
  1. TYPE a_type
  2.     foo AS STRING * 104

As a_type isn't mentioned subsequently. Is this really arrays in types?

If is, if you make ex your type and correct the typo.  :P
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #10 on: January 30, 2021, 11:45:02 am »
Sly edit. Okay.
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #11 on: January 30, 2021, 12:59:58 pm »
Code: QB64: [Select]
  1. PRINT LEN(a%), LEN(b) '>>>  2   8    hey this is handy!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #12 on: January 30, 2021, 02:24:15 pm »
This isn't really the answer that's being developed here, but here is an approach using nodes. Following is a demo that ends with a sum calculation over a bunch of numbers with mixed types:

Code: QB64: [Select]
  1. ' Version 2020-01-30
  2.  
  3.  
  4. REM $Include: 'SoftArrays-main.bi'
  5.  
  6. ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  7. ' Begin main program.
  8. ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  9.  
  10. _TITLE "Array Demo"
  11.  
  12. TYPE Person
  13.     NickName AS STRING
  14.     Age AS DOUBLE
  15.     Array1 AS LONG
  16.     Array2 AS LONG
  17.     ' etc.
  18.  
  19. DIM FellippeHeitor AS Person
  20.  
  21. ' Create a person.
  22. FellippeHeitor.NickName = "Fellippe"
  23. FellippeHeitor.Age = 27
  24. FellippeHeitor.Array1 = NewTextNode("Friends") ' Define two nodes to build from.
  25. FellippeHeitor.Array2 = NewTextNode("Numbers") '
  26.  
  27. ' Add data by linking nodes to the "east".
  28. a = FellippeHeitor.Array1
  29. a = LinkEast(a, NewTextNode("Luke"))
  30. a = LinkEast(a, NewTextNode("bplus"))
  31. a = LinkEast(a, NewTextNode("Ashish"))
  32.  
  33. a = FellippeHeitor.Array2
  34. a = LinkEast(a, NewIntegerNode(1))
  35. a = LinkEast(a, NewDoubleNode(3.14))
  36. a = LinkEast(a, NewIntegerNode(3))
  37.  
  38. ' Testing '''''''''''''''''''''''''''''''''''''''''''''''''''
  39.  
  40. ' Print each array
  41. PrintArray (FellippeHeitor.Array1)
  42. PrintArray (FellippeHeitor.Array2)
  43.  
  44. ' Sum the array containing numbers.
  45. ' However, the first node (as we have it) contains text!
  46. ' Must get the next-over address.
  47. a = FellippeHeitor.Array2
  48. b = Nodes(a).East
  49. PRINT SumNumbers(b) ' Prints 7.14, as expected
  50.  
  51. END '''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  52.  
  53. SUB PrintArray (x AS LONG)
  54.     DIM y AS LONG
  55.     PRINT Literal$(Nodes(x).Identity)
  56.     y = Nodes(x).East
  57.     IF (y <> -1) THEN CALL PrintArray(y)
  58.  
  59. FUNCTION SumNumbers (x AS LONG)
  60.     DIM TheReturn AS DOUBLE
  61.     DIM y AS LONG
  62.     TheReturn = LiteralNumber(Nodes(x).Identity)
  63.     y = Nodes(x).East
  64.     IF (y <> -1) THEN TheReturn = TheReturn + SumNumbers(y)
  65.     SumNumbers = TheReturn
  66.  
  67. ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  68. ' End main program.
  69. ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  70.  
  71. REM $Include: 'SoftArrays-main.bm'
  72.  

The required files will be on my github in a little while - just showing the method:

 
ss.png
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #13 on: January 30, 2021, 03:52:24 pm »
OK 

I redid original post using Luke's Method for numeric arrays but had a snag or 2, maybe just something I am miscalculating but had to drop drawing the first item in array strings. I made 2 array strings this time to easily distinguish X's from Y's in effort to shake off bug but didn't.

But it is nice not to have to pack and unpack items to and from arrays, just plug in values by index numbers into the "array" string.

The first method still holds for variable length strings in a "array" string.

Code: QB64: [Select]
  1. _TITLE "Getting an Array into UDT Mod 1 Luke Method" ' started b+ 2020-01-29
  2. ' 2021-01-30   Mod 1 employ Lukes method, dump ALL previous subroutines this is much less complex
  3. '  I am getting too many screwups trying x, y, x, y, x, y pairs! So do 2 string arrays 1 for X 1 for Y.
  4. '  Still getting some screwy problem from 1st and last elements???  but works fine if I ignore.
  5.  
  6. CONST Xmax = 1200, Ymax = 700
  7. TYPE ArrayType
  8.     Aname AS STRING ' here is the name of the array
  9.     X AS STRING '    here is the array joined into a long string so it may be stored in a UDT
  10.     Y AS STRING '
  11.  
  12. REDIM words(100) AS ArrayType ' record the script positions of handwritten words and save the word and positions into a UDT
  13.  
  14. SCREEN _NEWIMAGE(Xmax, Ymax, 32)
  15. _DELAY .25 'need time for screen to load before attempting to move it.
  16.  
  17. _TITLE "To gather data:  Hand write words with mouse and then enter the word, up to 100 words or escape."
  18. wi = 0 ' word index for words
  19.     CLS
  20.     mb = _MOUSEBUTTON(1): mx = _MOUSEX: my = _MOUSEY
  21.     IF mb THEN
  22.         words(wi).X = "": words(wi).Y = "": ai = 0 ' ai = array index for the elements in X, Y arrays
  23.         set words(wi).X, ai, mx: set words(wi).Y, ai, my: ai = ai + 1
  24.         PSET (mx, my)
  25.         WHILE mb
  26.             WHILE _MOUSEINPUT: WEND
  27.             mx = _MOUSEX: my = _MOUSEY: mb = _MOUSEBUTTON(1)
  28.             set words(wi).X, ai, mx: set words(wi).Y, ai, my: ai = ai + 1
  29.             LINE -(mx, my)
  30.             _LIMIT 20
  31.         WEND
  32.         CLS
  33.         INPUT "Enter word > "; w$
  34.         words(wi).Aname = w$
  35.         wi = wi + 1
  36.         IF wi > 100 THEN EXIT DO ' wi will always be 1 more than last word
  37.     END IF
  38. wi = wi - 1
  39.  
  40. ' simple playback
  41. FOR j = 0 TO wi
  42.     CLS
  43.     IF words(j).Aname <> "" THEN
  44.         PRINT "This is word "; words(j).Aname
  45.         FOR i = 1 TO LEN(words(j).X) / 2 - 1 '<<<< for some reason I have to dump 0 index
  46.             IF i = 1 THEN PSET (get%(words(j).X, i), get%(words(j).Y, i)) ELSE LINE -(get%(words(j).X, i), get%(words(j).Y, i))
  47.             _LIMIT 30
  48.         NEXT
  49.         _DELAY 1
  50.     END IF
  51.  
  52. '              ======================= Luke's Method ========================
  53.  
  54. SUB set (array$, index AS INTEGER, value AS INTEGER) ' <<<< having big debate with myself between Integer and Long, Integers are shorter
  55.     REDIM element_size AS INTEGER
  56.     element_size = LEN(value)
  57.     IF LEN(array$) < element_size * (index + 1) THEN
  58.         array$ = array$ + STRING$(element_size * (index + 1) - LEN(array$), CHR$(0))
  59.     END IF
  60.     MID$(array$, index * element_size + 1) = _MK$(LONG, value)
  61.  
  62. FUNCTION get% (array$, index AS INTEGER)
  63.     get% = _CV(INTEGER, MID$(array$, index * 2 + 1, 2))
  64.  
  65.  

OH! I missed changing a LONG to INTEGER!  be right back!
« Last Edit: January 30, 2021, 04:08:03 pm by bplus »

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: Demo: Getting arrays into an array of a UDT
« Reply #14 on: January 30, 2021, 04:05:54 pm »
Line 68 needs to say INTEGER.

I generally just accept the extra space usage of longs in order to get their extra range and prevent overflow, but to reach their own.