views:

5655

answers:

6

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?

+18  A: 
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
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
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
Hmm I didn't consider it might be a sanity check. That's certainly possible.
Whatsit
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 Galt
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 Galt
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
+5  A: 

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
I agree. If you need the functionality of an array and have dynamic length, an ArrayList is the most simple and obvious answer.
TheTXI
ArrayList? Are we allergic to generics?
Jim Mischel
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 Galt
Haha - generic are cool too. I was trying to keep it as close to what he was using as possible.
Brian
The webservice I have to pass this object to wants an array of InputProperty objects where there are no null entries.
John Galt
"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
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
+1  A: 

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;
    }
michl86
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 Galt
Never mind...got it now.
John Galt
+1  A: 

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
+1  A: 

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
A: 

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