I have a number of int? elements int? . Suppose this:

 int?[] row = new int?[] {0,null,1,2,3,0}; 

It is necessary for all elements that are equal to zero to create a certain structure (Suppose with the element index), discard the rest. The logic is something like this:

 MyStruct[] ar = row.Select( (el, ind) => el == 0 ? new MyStruct {index = ind} : null ) 

That is, for all zero elements we create a structure, for all the others we return null . Next, just filter out non-null elements, and turn everything into an array

 .Where(el => el != null).ToArray(); 

In the conditional line, an error is received:

Cant be no conditional conversion between Test.MyStruct and null

How to do right? May use a different approach? I want to use LINQ and not cycles.


Full text of the program

 using System; using System.Linq; public class Test { struct MyStruct { public int index; } public static void Main() { int?[] row = new int?[] {0,null,1,2,3,0}; MyStruct[] ar = row.Select( (el, ind) => el == 0 ? new MyStruct {index = ind} : null ).Where(el => el != null).ToArray(); } } 

or here

  • If we transfer the condition to Where and put it to Select , then the index of the element in the source array will be lost. If you pull the structure into a heap (convert to a class), it will not be very beautiful. The variant with dynamic also seems not too beautiful. How is more correct? - user200141
  • And it is precisely necessary struct? class is not suitable? - Zufir
  • one
    make a whole new file with a description of a whole new class - if you change the struct keyword to class in the current code, everything will work - Grundy
  • one
    What exactly should be the output? add an example of the result that is expected for the row array - Grundy
  • 2
    To declare another class inside another class — this is not too good — no worse than declaring another structure within a class. At the output, I need an array of structures for zero elements that contain some information about these elements. - add to the question a specific example of the array to be obtained - Grundy

5 answers 5

You can use a nullable structure:

 MyStruct?[] ar = row.Select( (el, ind) => el == 0 ? (MyStruct?)new MyStruct { index = ind } : null ).Where(el => el!=null).ToArray(); 

Looking at your code on IdeOne we get the following:

 struct MyStruct { public int index; } private static MyStruct?[] GetRow(int?[] row) { return row.Select( (el, ind) => el == 0 ? (MyStruct?)new MyStruct { index = ind } : null) .Where(el => el != null).ToArray(); } static void Main(string[] args) { int?[] row = new int?[] { 0, null, 1, 2, 3, 0 }; var ar = GetRow(row); ar.Select(el => { Console.WriteLine(el?.index); return el; }).ToArray(); Console.WriteLine(ar.Count()); } 

http://ideone.com/R0A6lJ


At the output of the GetRow method, we now get the structure Nullable<MyStruct> . It has two properties:

  • bool HasValue — Indicates whether this instance has a value;
  • MyStructure Value - contains the value itself (if, of course, it is).

Therefore, you cannot directly access the index field. You can write el.Value.index or use the new syntax (starting, it seems, with C # 6 and VS2015) and write el?.Index .

  • Yes, something and wanted. I myself have new MyStruct? ... to write only new MyStruct? ... new MyStruct? ... but that doesn't work. By your example, the query works, but I cannot use the index field further in the code. MyStruct Error? does not contain definitions for "index" and so on - user200141
  • one
    Well, the nullable struct is practically a class :-) - Grundy
  • @Zufir did not know about el?.Index , it really solved my problem. Thank you very much. - user200141
  • @Grundy Tell me please, what would you do? Would you carry it to a separate file? - user200141
  • @ user200141, a bunch of options, and everything depends on what you have to do with the result, where will it go? what actions will be with him? the easiest thing to do here was to use an anonymous class - Grundy

The problem you are facing is that the structure cannot be null . MyStruct? solution - use Nullable structure: MyStruct? .

Generally speaking, any value type can implicitly be cast to a nullable type. The problem is that the compiler is not yet able to guess that null in your ternary operator is just an empty nullable-type value, and not an object.

For reference: for nullable types, an empty value is only called null in c #, in fact, it is a well-defined value for an existing structure that has nothing to do with null for reference types.

Therefore, it is necessary to bring one of the branches of the ternary operator to MyStruct? obviously.

It turns out something like this: row.Select((el, ind) => el == 0 ? (MyStruct?)new MyStruct {index = ind} : null) or like this: row.Select((el, ind) => el == 0 ? new MyStruct {index = ind} : (MyStruct?)null) .

However, such a trick will change the type of the expression - and therefore you must add another Select to the end: .Select(x => x.Value) .

Together:

 row.Select((el, ind) => el == 0 ? new MyStruct {index = ind} : (MyStruct?)null).Where(x => x != null).Select(x => x.Value) 

Alternative options:

  1. Using SelectMany :

     row.Select((el, ind) => el == 0 ? new MyStruct[0] : new [] { new MyStruct {index = ind} }) 

    To skip an element, it returns an empty array. In order to return an element, we wrap it in an array of one element. Thus, the SelectMany method can replace Select and Where at the same time.

  2. Generator Usage:

     IEnumerable<MyStruct> TransformRow(IEnumerable<int?> numbers) { var index = 0; foreach (var number in numbers) { if (number == 0) { yield return new MyStruct { index = index }; } index++; } } // ... TransformRow(row).ToArray() 

    This method allows you to write more complex algorithms than linq to objects allows. And it is also easier to debug. The disadvantage of this method is its verbosity.

    Here's another option, without MyStruct? using the fact that the index is never negative. Semantically, of course, not very clean, but simply:

     int?[] row = new int?[] { 0, null, 1, 2, 3, 0 }; var result = row.Select((v, index) => v == 0 ? index : -1) // индекс хорошего элемента или -1 .Where(index => index >= 0) // отбросили плохие .Select(idx => new MyStruct() { index = index }); 

      Replace struct with class - and your code will work.

      I first made a class, but making a whole new file with a description of a whole new class, for the sake of only one index field, it seemed somehow ugly to me.

      Classes do not have to be declared in separate files. Nested classes are a completely normal language mechanism, just like nested structures. There are no practices that allow nested structures but prohibiting classes, no.

        Notice how simplified the task would be if you wanted to use a loop. Using Linq, and indeed any means, should be justified. Immediately you complicate your task, just to use Linq.

        In such cases, I would use an anonymous class.

          var ar = row.Select((x, i) => x == 0 ? new {value = x, index = i} : null).Where(x => x != null).ToArray(); 

        Then this anonymous class can be converted to any desired result using .Select()

        • The fact that I am still new to linq is not a linqa problem, but mine. Linq is a great tool, it was not stupid people who invented and implemented it. The task would be simplified (Only for me), but my level of knowledge did not grow. And the code would look worse - user200141