0

I have one 2D array that I'm wanting to populate into another. I got a bit mixed up between lists, dictionaries and simple arrays, so I'm thinking I have two different types or array. The edited code with some attempts and resulting errors:

    Dim _inifile As String = "C:\Users\Steve\Scripts\Projects\IniRecEdit\Tests\insrow.ini"

    Public IniLines() As String = File.ReadAllLines(_inifile)
    Public _ini(IniLines.Length - 1)() As String

    For I As Integer = 0 To IniLines.Length - 1
                _ini(I) = IniLines(I).Split("="c)
    Next

    '.....code

    Dim _tempini(Lines.Length - 1, SQSIZE - 1) As String
            Dim tagrow As Integer
            Dim tagcol As Integer
            Dim taglist() As String
            Dim RowSel As Integer = 1

     Dim cControl As Control
            For Each cControl In Me.Panel1.Controls
                If cControl.Tag <> Nothing Then
                    taglist = Split(cControl.Tag, ","c)
                    If taglist(0) = "Cell" Then
                        tagcol = Val(taglist(1))
                        tagrow = Val(taglist(2))
                        If tagrow <= RowSel Then
                            If tagcol = 0 Then
                                _tempini(tagrow, tagcol) = _ini(tagrow)(tagcol)
                                Debug.WriteLine("Lower or equal then Selected row. 1st Column. From ini to row:" & tagrow)
                ' EDIT etc etc... more code here           
            Next cControl

    ' DIFFERENT CODE TRIED AT THIS STAGE to transfer from one array to the other:
     ReDim _ini(Lines.Length - 1)
            For countrow As Integer = 0 To _tempini.GetLength(0) - 1
                For countcol As Integer = 0 To _tempini.GetLength(1) - 1
                    _ini(countrow) = _tempini(countrow)._tempini(countcol)
                Next
            Next
    ' Produces: Error   2   Number of indices is less than the number of dimensions of the indexed array.

    ReDim _ini(Lines.Length - 1)
    For countrow As Integer = 0 To _tempini.GetLength(0) - 1
                For countcol As Integer = 0 To _tempini.GetLength(1) - 1
                    _ini(countrow)(countcol) = _tempini(countrow, countcol)
                Next
            Next
    'Produces: Additional information: Object reference not set to an instance of an object.

As I say, I'm not even sure whether I'm using lists of not for "_ini" The Locals Window on Visual Studio shows the vars as:

_ini is "String()()"

_tempini is "String(,)"

I'm increasingly aware I need to go back to basics with vb and learn all of the concepts involved. However, a quick assist would let me try and complete this thing I'm knocking together with blu-tack and string :)

Steve Vinoski
  • 19,847
  • 3
  • 31
  • 46
stigzler
  • 793
  • 2
  • 12
  • 29
  • Arrays are generally sub-optimal for this sort of thing. It looks like tagrow , tagCol ...etc indicate some related data elements for some actor. Create a class and a List(of myFoo) or Dictionary. very similar: http://stackoverflow.com/q/27665113/1070452 And dont use Val. Ever. – Ňɏssa Pøngjǣrdenlarp Dec 28 '14 at 17:37
  • It would help to post a _sample_ of your `insrow.ini` file. Arrays are not very handy when you don't know how many items you'll have from the start (while they are really fast at runtime and easy to clone/copy) Perhaps the best move is to create a class with a different kind of data structure using `Dictionary` or `List(Of T)` – Karl Stephen Dec 28 '14 at 17:46
  • AndAlso : avoid implicit casting : `cControl.Tag -> String`. Use an actual String variable... in a few months, you'll forget the Tag Properties of your control is of type `String` and you'll waste minutes if not hours to figure that out. `Option Strict` would help alot figuring out most of your errors. – Karl Stephen Dec 28 '14 at 17:50
  • Thanks chaps - all good for learning! Looks like List(Of T) will be my approach next time. However, too deep in now to recode entirely, so still looking for a work around to get an array in one format into array in another... – stigzler Dec 28 '14 at 19:45

3 Answers3

0

You are actually using arrays. The declaration of a list would look like this:

Dim myList As New List(Of String)()

A list has always one dimension and the initial size of a list is 0 (unless you pass an enumeration to the constructor). You have to add items one by one using the Add method. This makes the list grow dynamically.


I assume that your ini-file looks like this

key1 = value1
key2 = value2
key3 = value3

Also I assume that the value does not contain an equal sign, otherwise I would look for the first instance of an equal sign an split the string there "manually".


Using a two dimensional array seems not appropriate in order to store key-value-pairs. There is a handy struct KeyValuePair(Of TKey, TValue) you could use if you need a string or a list. Using an array of KeyValuePairs has the advantage that you will not have to deal with two-dimensional arrays.

However, if you want to look up values for given keys, the Dictionary(Of TKey, TValue) is more appropriate, as it is optimized for a very fast lookup.

Dim dict As New Dictionary(Of String, String)

For i As Integer = 0 To IniLines.Length - 1
    Dim parts = IniLines(i).Split("="c)
    dict.Add(parts(0).Trim(), parts(1).Trim())
Next

Then you can retrive a value like this

Dim value As String
If dict.TryGetValue("some key", value) Then
    ' Use the value
Else
    ' Sorry, key does not exist
End If

Or if you are sure that the key you are looking for really exists, you can simply write

value = dict("some key")
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • Thanks Oliver. As per my reply to OP above - I will know for next time. Yep - in key value pair format, but also manipulating these with matching schema arrays etc. However, List(Of T) it is next time! Still need to find a hack with my present situation, however. – stigzler Dec 28 '14 at 19:47
0

TLDR

(1) Working with Arrays...
(2) Why are you getting "Object reference not set to an instance of an object."
(3) Multidimensional Arrays and Arrays of Arrays
(4) Why are you getting "Number of indices is less than the number of dimens..."
(5) What I would do if I were you...


Working with Arrays : Declaration(1) and Instanciation(2)

(1) declaration is when you write the name of a variable and its type. You can then access the object reffered by this variable in your code.

(2) we can talk about (variable/object) instanciation when an actual object has been created and registered somewhere in the memory. You can then manipulate that object via something that points to that object : the variable.

A declaration doesn't always mean the actual object is created. (I won't dig in ValueType and ReferenceType, sorry guys, I'm just cutting corners while explaining things without the complex verbose of strict dotNet)


Arrays declaration is different from instanciation :

Dim myArray As String()

This is just a declaration. Your Array is Nothing at this time and therefore contains nothing. Calling something like myStringVariable = myArray(0) will throw an Exception (Object reference not set to an instance of an object) meaning your array declaration points to nowhere because you haven't created an object of type String() and made your Array declaration points to that object.

Dim myArray() As String

This is the same declaration as above. Which one is correct ? I don't know and it's a matter of taste. VB is known for letting you do things the way you want. If you're good, you'll be able to use the one or the other like breathing. In fact, I really don't care.

Dim myArray As String() = new String() {}

This is just an instanciation of your array. Your array is registered in memory, but is of length 0 (zero) and contains no single item (Length = 0).

Remeber the declaration above ? (myArray() As String or myArray As String()) In fact, there is a difference in the two syntax when you talk about instanciation :

  • you can write : Dim myArray(3) As String
  • but you can't write : Dim myArray As String(3)

The later throws an exception. The variable type (String) cannot have indexes delimiters.

Dim myArray(3) As String '...

... also declares an Array and instanciate it with 4 predefined items like below.

Dim myArray As String() ' this is a declaration.
Redim myArray(3) ' this is an instanciation with a defined size.

This is one another way to define the number of items in your array using the Redim keyword. You have 4 "slots" in that Array from index [0] to index [3]. Your array looks like this :

[0][""]
[1][""]
[2][""]
[3][""]

.

Dim myArray As String = New String(3) {}

This is the same as above, but in one line : you're declaring and instanciating an Array of String that contains 4 items of type String which are all empty string (they are not Nothing)

Dim myArray() As String = { "One", "Two", "Three", "Four" }

This is a one line declaration, instanciation and items-setting :

[0]["One"]
[1]["Two"]
[2]["Three"]
[3]["Four"]

Redim

Redim is used to redifine the number of items in an Array. One dimensional arrays just behaves the way they are supposed to.

Suppose you have defined your array as :

Dim myArray() As String = { "One", "Two", "Three", "Four" }

Obviously, you have four String. Then use Redim :

Redim myArray(6) ' to get 7 items ..!

This is what you get :

[0][""]
[1][""]
[2][""]
[3][""]
[4][""]
[5][""]
[6][""]

You can resize an Array, but to keep the items inside, you'll have to use a keyword : Preserve

Redim Preserve myArray(6)
myArray(4) = "Five"
'myArray(5) = "Six"
myArray(6) = "Seven"

Because I've commented out the setting of the sixth item, the array will look like this :

[0]["One"]
[1]["Two"]
[2]["Three"]
[3]["Four"]
[4]["Five"]
[5][""] ' Empty String.
[6]["Seven"]

What happens if you Redim Preserve the Array above twice while killing some items ?

Redim Preserve myArray(2)
Redim Preserve myArray(6)

' You get :

[0]["One"]
[1]["Two"]
[2]["Three"]
[3][""]
[4][""]
[5][""]
[6][""]

You've lost items 3..6 ! Don't expect to get them back.


Public _ini(IniLines.Length - 1)() As String

What this code does ? Writing Dim myArray(3)() As String ("3" could be any positive or null integer) results in the following Array structure :

[0][Nothing]
[1][Nothing]
[2][Nothing]
[3][Nothing]

Why are you getting Nothing but not a bidimensional array ? Not even empty strings ?
It's because the declaration above creates a ONE dimensional Array of Array (of the type defined, String here)

VB.net allows you to declare an Array of Array this way (I don't know yet if C# can - but I really don't care : I never, and will never use this type of syntax) This is not a bidimensional Array. To define the values, you'll have to create each instance of the contained Array per item (or line) and assign it to your Array.

In the declaration above, while you have explicitely initialized your Array, its contents [0..3] are still simple declarations that points to no instance of anything, thus, explaining the "Nothing" thing.

Then upon getting datas from your file, your _ini variable could look like this, depending on the content of your file (that's why I asked you to provide a sample of the content of your file) :

[0][ ["Key0" | "Value0"] ]
[1][ ["Key1" | ""] ]
[2][ [""] ]
[3][ ["Key3" | "Value3" | "OtherData" | "MoreData" | "EvenMoreData"] ]
...

Reading a (String) value can be done like follows :

Dim myStringVariable As String = _ini(0)(1) ' Gets "Value0"

=> Be carefull when you use Array or Arrays along with multidimentional arrays. They are not the same !

Let's pick the code below :

        ReDim _ini(Lines.Length - 1)
        For countrow As Integer = 0 To _tempini.GetLength(0) - 1
            For countcol As Integer = 0 To _tempini.GetLength(1) - 1
                _ini(countrow)(countcol) = _tempini(countrow, countcol)
            Next
        Next

Your _tempini is a bidimentional array of String.
Your _ini is an one dimensions array of array of String.
Scan this line :

_ini(countrow)(countcol) = _tempini(countrow, countcol)

1) _ini(countrow) contains Nothing (whatever the value of countrow) = contains NO Array (of String) because you've just called ReDim _ini(Lines.Length - 1) right before the For loop.

2) Therefore your assignation _ini(countrow)(countcol) = ... is the same as running the following at runtime :

Nothing(countcol) = _tempini(countrow, countcol)

Nothing is not an Array, so the (countcol) indexing has no meaning : you're indexing an item value to an object expected to be an Array that doesn't even exist.

That's why you get the Exception Object reference not set to an instance of an object.


Multidimensional Arrays / Redim / Preserve

The declaration and instanciation above still apply !

Public myBiDiArray(,) As String ' Declaration ONLY : bi dimensional array
Public myBiDiArray(2,1) As String ' Declaration AND Instanciation
Public myBiDiArray(,) As String = New String(2, 1) {} ' Dec. + Inst.
Public myBiDiArray(,) As String = _
    { {"One", "Un"}, {"Two", "Deux"}, {"Three", "Trois"} }
    ' ^^ Declaration + Instanciation + Item Setter
Public myBiDiArray(,) As String = _
    New String(,) { {"One", "Un"}, {"Two", "Deux"}, {"Three", "Trois"} }
    ' ^^ Declaration + Instanciation + Item Setter

Let's have a Bidimensional Array of String with rows and columns...

Dim myArray(2, 1) As String ' 3 lines, two columns

This array contains the following items :

Line 0 = [""][""]
Line 1 = [""][""]
Line 2 = [""][""]

Now you want to resize the Array and use the Redim (only) Keyword...

Redim myArray(5, 3) ' ...

...which would set the lines to 6 and columns to 4 :

' Array Size = 6, 4
Line 0 = [""][""][""][""]
Line 1 = [""][""][""][""]
Line 2 = [""][""][""][""]
Line 3 = [""][""][""][""]
Line 4 = [""][""][""][""]
Line 5 = [""][""][""][""]

That's great !
Now let's set a value in Row(0) and Column(0)

myArray(0, 0) = "Cell,0,0"

' You get :
Line 0 = ["Cell,0,0"][""][""][""]
Line 1 = [""]        [""][""][""]
Line 2 = [""]        [""][""][""]
Line 3 = [""]        [""][""][""]
Line 4 = [""]        [""][""][""]
Line 5 = [""]        [""][""][""]

But what happens with the Preserve Keyword ?

Redim Preserve myArray(3, 2) ' 4 lines and 3 columns

You get an exception : 'ReDim' can only change the rightmost dimension.

But you must know that this Exception is not handled by the debugger unless you explicitly write a Try/Catch routine that encapsulates the Redim Preserve Code. Otherwise, the application just exit the method/function containing that piece of code without any warning and your Array remains untouched !

To redim a multidimensional array (not only the last dimension) while preserving its content, you must create a new array of the desired size and copy the content of the former one to this new array, then make your former array point to that new one...


_ini(countrow) = _tempini(countrow)._tempini(countcol)

I'm not aware of such syntax in VB.Net. Perhaps you got this from another language or a Reference that adds extensions to Arrays (like System.Linq) which extends (?) the members of an Array to itself for some purpose.., but I'm not aware of that.

It looks to me like a simple syntax error. That's why you get the Exception Number of indices is less than the number of dimensions of the indexed array.

To get the (string) value of a bidimensional array, just write :

myStringValue = _tempini(countrow, countcol)

The error warning is not pointing to the systax error though : the debugger stopped at the closing backet and discarded the remaining piece of text :

_ini(countrow) = _tempini(countrow ... ' <- compiler expects a comma here.

Writing this will produce the following error :

myStringValue = _tempini(countrow, countcol)._tempini(countcol)

Compiler Error : 'tempini' is not a member of 'String'.

Again, don't mess with bidimentional arrays and arrays of arrays : Writing...

_ini(countrow) = _tempini(countrow, countcol) ' ...

... has no meaning ! Even if the syntax looks correct, the above code actually tries to assign a String value to an Array(Of String) variable, which will throw an InvalidCastException.


I'll be honest : Arrays are a great bit of fun with fixed/predefined size/dimensions. From the moment I'm working with unknown number of elements/items at runtime, I leave Arrays behind.

What I would do ?

Oh! just the same Olivier Jacot-Descombes advised you to do :) No surprise, a Dictionary is very handy in your situation.

I would also enable those two :

  • Option Strict On
  • Option Explicit On

and disable this one : Option Infer Off.

Another PERSONAL advice is to avoid using the Tag property of a control. This tag is of type Object.

  • some Casting should be done to get a cleaner code.
  • Controls are usual components you'll use anywhere. One day, I've imagined I have tied an object of type 'String' to a specific control while it actually contained a Date or even Nothing. Why ? Because I confounded two different projects and lost track of the whole thing.
  • pass your code to a colleague or a friend, and be sure he/she'll ask you to document it about what you've done with the Tag Property... that simply means no one want to discover the obscure underlying logic of a code involving Objects. People like to deal with strongly typed variables, easy to debug and fast performing at runtime.

Also avoid the use of Val(). Val() is very handy because it can almost convert anything. However, because of this nice feature, it adds a workload on the CPU. Again, if you know you're dealing with Int32 or Double, then use Int32.Parse() or Double.Parse() but not Val(). Most misuse of Val() on the web are related to type inference and implicit casting. Debugging such kind of glitches are a real pain. Use Val only when you have no alternative.


I think you now have almost everything you need to get your code working with little changes. I have not provided the code for the changes though :

  • I don't understand where your samples of code lands in your application logic
  • I don't know what kind of datas you're manipulating
  • I don't even know what's the purpose of the code.

^^ So I just addressed two things :

  • why you're getting errors...
  • since you're new to VB, I just provided a custom explanation of Arrays.

Was that the (not so) quick assist you were looking for ? I don't know. That's the best I could give right now.

Community
  • 1
  • 1
Karl Stephen
  • 1,120
  • 8
  • 22
  • Wow - that was an amazing assist - thank you for the time and effort put into that. I will revisit when I'm less red-eyed and tired! I'm presently moving from beginners luck to awkward amateur, so I'm guessing I'm gonna have to start putting some hours into research and understanding. Thanks again. – stigzler Dec 28 '14 at 23:21
  • OK. Had time to read trough this + thanks for the full and custom response. Certainly very helpful with my learning and realizing how much I need to get my head around. Very informative. Did help in the eventual resolution. However, too deep into the code to totally recode it, so had to find a way to work with what I've got. It aint pretty, but hey, it works. :) I'll know better for next time. – stigzler Dec 30 '14 at 22:03
0

OK. People have been really helpful, but not really up to totally recoding at this stage. I did find a work around, however. It aint pretty and probably is really bad practice! However, got it up and running with the code I've got.

Essentially, I just did the process in reverse, constructed a line from the components in _tempini, delimited by a "=" and then just re-ran the original procedure for populating the _ini array in the first place. A total work-around, but hope will help any other amateurs like me getting stuck in these early stages.

However, I would say follow these fellas advice! Look into list, dictionaries etc before starting your code.

The fix:

 ReDim _ini(((_tempini.Length) / 2) - 1)
        Dim _newiniline As String
        _newiniline = ""
        For countrow As Integer = 0 To (((_tempini.Length) / 2) - 1)
            For countcol As Integer = 0 To 1
                _newiniline = _newiniline & _tempini(countrow, countcol)
                If countcol = 0 Then   
                        _newiniline = _newiniline & "="       
                End If
                _ini(countrow) = _newiniline.Split("="c)
            Next
            _newiniline = Nothing
        Next
stigzler
  • 793
  • 2
  • 12
  • 29