1

I have the following terrible code:

CurrentInvoice = invoiceTable.AsEnumerable()
                    .First().ItemArray
                    .Where(a == null, a = (int a).Tostring("null"))
                    .Select(i => i.ToString())
                    .ToArray();

I'm trying to use the "Where" method to pass any null values as a physical string that reads "null", without breaking the rest of the line. Am I going to get anywhere using the "Where" method?

Omar
  • 16,329
  • 10
  • 48
  • 66
  • 1
    So what's the current issue with the code you have? – tnw Jun 30 '14 at 13:33
  • 3
    Seems more like a `Select` than a where. – vcsjones Jun 30 '14 at 13:33
  • @tnw Quite bluntly, it doesn't work. Potentially, any null values might/would cause a completely missing element when I return updated data back to the table. –  Jun 30 '14 at 13:35
  • 1
    The where clause is missing the lambda `.Where(a => a....)`? – Sayse Jun 30 '14 at 13:35
  • @vcsjones No, I'm not so sure. There aren't very many null values (most of them contain data), but a handful do exist. I'm not quite sure how `Select` would work –  Jun 30 '14 at 13:36
  • @Sayse Unfortunately that doesn't work: `Cannot convert lambda expression to delegate type 'System.Func' because some of the return types in the block are not implicitly convertible to the delegate return type` –  Jun 30 '14 at 13:38
  • This makes no sense. You're just looking at the first row of the DataTable, is that desired? – Tim Schmelter Jun 30 '14 at 13:38
  • 2
    My point is, your question has invalid syntax, and really, your `select` should be doing this instead as stated by vcsjones – Sayse Jun 30 '14 at 13:39
  • 1
    It's very unclear what you're trying to do here. `Where` is for *filtering*. How does that fit with "pass any null values as a physical string that reads 'null'"? – Jon Skeet Jun 30 '14 at 13:39
  • You can chain where clauses by using logical operators like `&&` or `||`. Using a comma is invalid syntax. The same goes for `=` If you want to check for equality, use the equality operator `==` – Marco Jun 30 '14 at 13:41
  • @JonSkeet I was thinking along the lines of looking for null values, for example: `Where 2 = 2, return two`. If I used Select, wouldn't that have to be _after_ I added the data to the array? At which point, the exercise would be rendered pointless. –  Jun 30 '14 at 13:42
  • 1
    if that is `DataTable` (which the `.ItemArray` strongly suggests), then I'm not sure that `null` is even going to be there - it'll be `DBNull.Value`, surely? – Marc Gravell Jun 30 '14 at 13:42
  • 1
    @RoteKatze: But where doesn't have "return two" - it just has "return true or false", because it's a filter. – Jon Skeet Jun 30 '14 at 13:43
  • @MarcGravell It's a DataSet. Does the same apply? –  Jun 30 '14 at 13:46
  • @JonSkeet Ah, I understand. PulseLab's answer below works, but I appreciate the explanation. –  Jun 30 '14 at 13:46
  • 1
    @RoteKatze same thing, essentially (plus: I suspect `invoiceTable` is actually a `DataTable`, not a `DataSet`) – Marc Gravell Jun 30 '14 at 13:50
  • @MarcGravell Ah, yes, My mistake. Can `DBnull.Value ` be used here? I've used `...mArray.Select(a => a ?? "null").Select(i =...` here for simplicity. –  Jun 30 '14 at 13:52
  • 1
    @RoteKatze if you mean "can the value be `null`?" - then [yes it can](http://pastie.org/9340554) (outputs `{DBNull}`). However, the null-coalescing operator (`??`) does **not** work with `DBNull` (because a `DBNull` is not `null`), so the `a => a ?? "null"` does **not** actually work reliably, based on that evidence – Marc Gravell Jun 30 '14 at 14:00
  • @MarcGravell So DBNull is technically a value? –  Jun 30 '14 at 14:13
  • 1
    @RoteKatze don't get me started... http://stackoverflow.com/a/9632050/23354 – Marc Gravell Jun 30 '14 at 14:33
  • @MarcGravell Oh my. Does that mean I need not worry about DBNull here, and Null will suffice? I only ask because I now need to work on a method to return the array data to the dataset, subsequently to the database. I ought to mention I'm using a string array. –  Jun 30 '14 at 14:58
  • 1
    @RoteKatze yes, as per the example, you definitely need to worry about `DBNull`. I can't remember whether you *also* need to worry about `null` - but this needn't be hard; will update answer to illustrate – Marc Gravell Jun 30 '14 at 15:00

3 Answers3

4

The Where statement filters the array, but you need to do a transform on each cell, which is a Select statement.

You need something like .Select(a => a ?? "null") instead of the Where I think?

PulseLab
  • 1,577
  • 11
  • 15
2

I don't understand what you're really asking for, but maybe you need something like:

CurrentInvoice = invoiceTable.AsEnumerable()
                    .First().ItemArray
                    .Select(x => x == null ? "null" : x) //or x ?? "null" for more info check the null coalescing operator
                    .ToArray();

So you "convert" string from null to "null". As you see you don't need to filter the ItemArray by using the Where() statement.

Omar
  • 16,329
  • 10
  • 48
  • 66
1

I think you're over-complicating it by trying to use LINQ here; this looks like a DataTable's first row, in which case:

object[] arr = invoiceTable.Rows[0].ItemArray;
for(int i = 0 ; i < arr.Length ; i++) {
    if(arr[i] == null || arr[i] is DBNull) arr[i] = "null";
}

This is easier to write, easier to understand, and much more efficient.

Edit: if the final result is (comments) intended to be a string[], then:

object[] arr = invoiceTable.Rows[0].ItemArray;
string[] result = Array.ConvertAll(arr,
    val => (val == null || val is DBNull) ? "null" : val.ToString());
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Hi Marc, interested in your comment that this method is more efficient. I had always assumed that under the hood, the LINQ statements would compile down to essentially the same logical flow in a situation such as thing. Is this not the case? – PulseLab Jun 30 '14 at 13:51
  • 3
    @PulseLab very much not the case; first we consider the `.AsEnumerable().First()` - that has to create an enumerator/iterator, to do what is already directly available via an indexer. The `Select` involves a delegate, the `ToArray` involves another enumerator/iterator, and an array creation (actually, probably several: at least one oversized, then finally one right-sized). Since the `.ItemArray` is *already* a new array instance per `get`, we can simply avoid all that and mutate the result of `.ItemArray`; far far cheaper. No iterators; no enumerator; no delegates; no abstractions; one array. – Marc Gravell Jun 30 '14 at 13:55
  • 1
    (+1) Thanks for the explanation, useful to bear in mind for the future. – PulseLab Jun 30 '14 at 13:57
  • Can I replace `CurrentInvoice = invoiceTable.AsEnumerable().First().ItemArray .Select(a => a ?? "null").Select(i => i.ToString()).ToArray();` with `object[] arr = invoiceTable.Rows[0].ItemArray; CurrentInvoice = Array.ConvertAll(arr, val => (val == null || val is DBNull) ? "null" : val.ToString());`? It appears to work in the IDE, but I'm not sure whether this will affect the return journey (array > dataset > data adaptor > database> –  Jun 30 '14 at 15:06
  • @RoteKatze once you're using ItemArray, any correlation to the original data is lost. ItemArray is always a standalone copy of the data – Marc Gravell Jun 30 '14 at 17:19