30

I am still new to C# and I've been struggling with various issues on arrays. I've got an array of metadata objects (name value pairs) and I would like to know how to create only the number of "InputProperty" objects that I truly need. In this loop I've arbitrarily set the number of elements to 20 and I try to bail out when the entry becomes null but the web service on the receiving end of this does not like any null elements passed to it:

private Update BuildMetaData(MetaData[] nvPairs)
{
    Update update = new Update();
    InputProperty[] ip = new InputProperty[20];  // how to make this "dynamic"
    int i;
    for (i = 0; i < nvPairs.Length; i++)
    {
        if (nvPairs[i] == null) break;
        ip[i] = new InputProperty();
        ip[i].Name = "udf:" + nvPairs[i].Name;
        ip[i].Val = nvPairs[i].Value;
    }
    update.Items = ip;
    return update;
}

In summary, say I only have 3 namevalue pairs in the above input array? Rather than allocate 20 elements for the array called ip, how can code this so ip is only as big as it needs to be. The update object is passed across another webservice so serialization is important (i.e. I can't use namevaluecollection, etc.).

p.s. Is the only way to followup on a posted question through the "add comments" facility?

John Adams
  • 4,773
  • 25
  • 91
  • 131

9 Answers9

33
InputProperty[] ip = new InputProperty[nvPairs.Length]; 

Or, you can use a list like so:

List<InputProperty> list = new List<InputProperty>();
InputProperty ip = new (..);
list.Add(ip);
update.items = list.ToArray();

Another thing I'd like to point out, in C# you can delcare your int variable use in a for loop right inside the loop:

for(int i = 0; i<nvPairs.Length;i++
{
.
.
}

And just because I'm in the mood, here's a cleaner way to do this method IMO:

private Update BuildMetaData(MetaData[] nvPairs)
{
        Update update = new Update();
        var ip = new List<InputProperty>();

        foreach(var nvPair in nvPairs)
        {
            if (nvPair == null) break;
            var inputProp = new InputProperty
            {
               Name = "udf:" + nvPair.Name,
               Val = nvPair.Value
            };
            ip.Add(inputProp);
        }
        update.Items = ip.ToArray();
        return update;
}
BFree
  • 102,548
  • 21
  • 159
  • 201
  • I think you're missing the problem here -- the input array, nvPairs, contains null values after the useful ones. Simply using its length won't solve anything. – Whatsit Mar 25 '09 at 19:38
  • The OP never specified that. I just assumed the if null was a simple sanity check. If what you're saying is in fact the case, then you're right. List would definitely be the way to go. – BFree Mar 25 '09 at 19:46
  • Hmm I didn't consider it might be a sanity check. That's certainly possible. – Whatsit Mar 25 '09 at 19:49
  • Whatsit is correct (input array nvPairs contains null entries after the useful ones. I tried the "cleaner" way offered by BFree above with slight change to get around compile time errors. – John Adams Mar 25 '09 at 20:54
  • If array called ip is allocated same length as nvPairs, how does the above code get array ip populated with objects. I see inside the foreach loop the creation of an InputProperty object, but I don't see how these objects get added to array ip. – John Adams Mar 25 '09 at 20:56
  • OK, I edited my post. It's now using a list, and correctly adding the new InputProperty objects. I'm typing the code directly into the answer box, so if there are syntax errors, I aplogize. – BFree Mar 25 '09 at 21:14
24

If you don't want to use a List, ArrayList, or other dynamically-sized collection and then convert to an array (that's the method I'd recommend, by the way), then you'll have to allocate the array to its maximum possible size, keep track of how many items you put in it, and then create a new array with just those items in it:

private Update BuildMetaData(MetaData[] nvPairs)
{
    Update update = new Update();
    InputProperty[] ip = new InputProperty[20];  // how to make this "dynamic"
    int i;
    for (i = 0; i < nvPairs.Length; i++)
    {
        if (nvPairs[i] == null) break;
        ip[i] = new InputProperty(); 
        ip[i].Name = "udf:" + nvPairs[i].Name;
        ip[i].Val = nvPairs[i].Value;
    }
    if (i < nvPairs.Length)
    {
        // Create new, smaller, array to hold the items we processed.
        update.Items = new InputProperty[i];
        Array.Copy(ip, update.Items, i);
    }
    else
    {
        update.Items = ip;
    }
    return update;
}

An alternate method would be to always assign update.Items = ip; and then resize if necessary:

update.Items = ip;
if (i < nvPairs.Length)
{
    Array.Resize(update.Items, i);
}

It's less code, but will likely end up doing the same amount of work (i.e. creating a new array and copying the old items).

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
8

Use this:

 Array.Resize(ref myArr, myArr.Length + 5);
Peter DeWeese
  • 18,141
  • 8
  • 79
  • 101
kilan
  • 81
  • 1
  • 1
5

Does is need to be an array? If you use an ArrayList or one of the other objects available in C#, you won't have this limitation to content with. Hashtable, IDictionnary, IList, etc.. all allow a dynamic number of elements.

Brian
  • 4,931
  • 3
  • 32
  • 55
  • I agree. If you need the functionality of an array and have dynamic length, an ArrayList is the most simple and obvious answer. – TheTXI Mar 25 '09 at 19:35
  • ArrayList? Are we allergic to generics? – Jim Mischel Mar 25 '09 at 19:42
  • michl86 comments that use of List and .ToArray at the end is too slow. Looks like ArrayList can be converted to array in same manner. For my final result I need an array not ArrayList. Is performance that bad with .ToArray() just before return? – John Adams Mar 25 '09 at 19:44
  • Haha - generic are cool too. I was trying to keep it as close to what he was using as possible. – Brian Mar 25 '09 at 19:46
  • The webservice I have to pass this object to wants an array of InputProperty objects where there are no null entries. – John Adams Mar 25 '09 at 19:46
  • "Too slow" is relative, and in this case irrelevant. It's highly unlikely that the time required for ToArray() will make a dent in the latency between your application and the Web service. – Jim Mischel Mar 25 '09 at 19:56
  • I don't think @michl86 meant "too slow" as in performance, but instead meant "too slow" as in @BFree beat him to the answer. – Michael Meadows Mar 25 '09 at 19:56
2

Use Array.CreateInstance to create an array dynamically.

    private Update BuildMetaData(MetaData[] nvPairs)
    {
        Update update = new Update();
        InputProperty[] ip = Array.CreateInstance(typeof(InputProperty), nvPairs.Count()) as InputProperty[];
        int i;
        for (i = 0; i < nvPairs.Length; i++)
        {
            if (nvPairs[i] == null) break;
            ip[i] = new InputProperty();
            ip[i].Name = "udf:" + nvPairs[i].Name;
            ip[i].Val = nvPairs[i].Value;
        }
        update.Items = ip;
        return update;
    }
Johannes
  • 6,490
  • 10
  • 59
  • 108
2

You could use List inside the method and transform it to an array at the end. But i think if we talk about an max-value of 20, your code is faster.

    private Update BuildMetaData(MetaData[] nvPairs)
    {
        Update update = new Update();
        List<InputProperty> ip = new List<InputProperty>();
        for (int i = 0; i < nvPairs.Length; i++)
        {
            if (nvPairs[i] == null) break;
            ip[i] = new InputProperty();
            ip[i].Name = "udf:" + nvPairs[i].Name;
            ip[i].Val = nvPairs[i].Value;
        }
        update.Items = ip.ToArray();
        return update;
    }
Michael Piendl
  • 2,864
  • 1
  • 27
  • 21
  • Adapted code above. Getting "The type or namespace name 'List' could not be found (are you missing a using directive or an assembly reference?) I have this too (what else could he want): using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; – John Adams Mar 25 '09 at 19:54
2

Or in C# 3.0 using System.Linq you can skip the intermediate list:

private Update BuildMetaData(MetaData[] nvPairs)
{
        Update update = new Update();
        var ip = from nv in nvPairs
                 select new InputProperty()
                 {
                     Name = "udf:" + nv.Name,
                     Val = nv.Value
                 };
        update.Items = ip.ToArray();
        return update;
}
dahlbyk
  • 75,175
  • 8
  • 100
  • 122
0

You can create an array dynamically in this way:

 static void Main()
    {
        // Create a string array 2 elements in length:
        int arrayLength = 2;
        Array dynamicArray = Array.CreateInstance(typeof(int), arrayLength);
        dynamicArray.SetValue(234, 0);                              //  → a[0] = 234;
        dynamicArray.SetValue(444, 1);                              //  → a[1] = 444;
        int number = (int)dynamicArray.GetValue(0);                      //  → number = a[0];


        int[] cSharpArray = (int[])dynamicArray;
        int s2 = cSharpArray[0];

    }
komizo
  • 1,052
  • 14
  • 21
0

Typically, arrays require constants to initialize their size. You could sweep over nvPairs once to get the length, then "dynamically" create an array using a variable for length like this.

InputProperty[] ip = (InputProperty[])Array.CreateInstance(typeof(InputProperty), length);

I wouldn't recommend it, though. Just stick with the

List<InputProperty> ip = ...
...
update.Items = ip.ToArray();

solution. It's not that much less performant, and way better looking.

Michael Meadows
  • 27,796
  • 4
  • 47
  • 63