1

I have a piece of C# code where I have created an anonymous type object as follows:

    var measurementUnits = new List<dynamic>() {
    new { Unit = "SQF", Display = new List<string>() { "F", "FT", "SQ FT" }, Ratio=1.5 } ,
    new { Unit = "Hectares", Display = new List<string>() { "H", "HEC"} , Ratio=2.5},
    new { Unit = "Acres", Display = new List<string>() { "AC(TO)" } , Ratio=3.5},
    new { Unit = "SQM", Display = new List<string>() {  "M", "SQ M"}, Ratio=4.5 }
};

Through LINQ I want to access the Ratio where Display="HEC" (case-insensitive) something like:

var multiplier = measurementUnits.Where(m => m.Display == "HEC").First().Ratio;
2
  • 6
    Is there any reason you're using dynamic here at all? I don't see anything dynamic in what you're doing... why not just create a list of the anonymous type, and keep all the benefits of static typing? Commented Feb 1, 2019 at 9:57
  • 1
    Btw, "H" or "HEC" . You code and description doesn't quite match Commented Feb 1, 2019 at 10:04

6 Answers 6

4

One possible solution is to avoid dynamic keyword. Try this code

var measurementUnits = new [] {
    new { Unit = "SQF", Display = new List<string>() { "F", "FT", "SQ FT" }, Ratio=1.5 } ,
    new { Unit = "Hectares", Display = new List<string>() { "H", "HEC"} , Ratio=2.5},
    new { Unit = "Acres", Display = new List<string>() { "AC(TO)" } , Ratio=3.5},
    new { Unit = "SQM", Display = new List<string>() {  "M", "SQ M"}, Ratio=4.5 }
};

var multiplier = measurementUnits.Where(m => m.Display.Contains("HEC")).First().Ratio;

Also it would be better to replace First with FirstOrDeafult and manually test it for null, to avoid NullReference exception

var unit = measurementUnits.FirstOrDefault(m => m.Display.Contains("HEC"));
if (unit != null)
    var multiplier = unit.Ratio();    
Sign up to request clarification or add additional context in comments.

7 Comments

Only answer that avoids an exception if no such display is contained and also handles that case properly. But you could replace Where with FirstOrDefault(..) to make it even more concise.
@Rango thank you. I believe that we already have enough duplicates of "What is NullReference?" :)
What if I need a case-insensitive search like Contains("HeC") ?
@Sujoy: pass StringComparer.CurrentCultureIgnoreCase, then you aren't using List.Contains anymore but LINQ's extension method Enumerable.Contains
@Rango you are wrong, I get error No overload for method 'Contains' takes '2' arguments'
|
1

Contains would be your best option, yet static typing would be better and not using dynamic.

What is the difference between statically typed and dynamically typed languages?

var measurementUnits = new List<dynamic>() {
     new { Unit = "SQF", Display = new List<string>() { "F", "FT", "SQ FT" }, Ratio=1.5  ,
     new { Unit = "Hectares", Display = new List<string>() { "H", "HEC"} , Ratio=2.5 },
     new { Unit = "Acres", Display = new List<string>() { "AC(TO)" } , Ratio=3.5},
     new { Unit = "SQM", Display = new List<string>() { "M", "SQ M"}, Ratio=4.5 } 
};

var multiplier = measurementUnits.Where(m => m.Display.Contains("HEC")).First().Ratio;

Also you will need to check multiplier for null as your using First this will throw a null reference exception if no value is found, better solution:

var multiplier = measurementUnits.Where(m => m.Display.Contains("HEC")).FirstOrDefault()?.Ratio;

Comments

1

Technically you can put something like this (please, note, that Display is a collection which can Contain but not be equal to a single item "H"):

  // 2.5
  var multiplier = measurementUnits
    .First(item => item.Display.Contains("H"))
    .Ratio;

However, I recommend to use a custom class for this, not dynamic which is prone for runtime errors (what if Display is a string, not List<string>).

Edit: If there's a possibility that there's no such an item (say, "HEC2") and you don't want exception be thrown but a default value, change First to FirstOrDefault:

  // 0.0 - default ratio, since "HEC2" is not found
  var multiplier = measurementUnits
    .FirstOrDefault(item => item.Display.Contains("HEC2"))   
   ?.Ratio ?? 0 /* Default Ratio Value Here */;

3 Comments

You get an exception if there is no such display contained
@Rango: yes, and I think it's a desired behavior (since First() is in the OP's original code)
There is also Where(m=>m.Display=="HEC"), so this is just pseudo code. I wouldn't draw any conclusions from there. As Aleks has said in another comment: " I believe that we already have enough duplicates of What is a NullReferenceException, and how do I fix it?"
0

I believe you want to list where Display "has" "HEC", since it is a list. PS: It is unclear in your OP whether you want to filter by "H" or "HEC" (code and description dont match). Assuming it to be "HEC"

var result = measurementUnits.Where(x=>x.Display.Contains("HEC")).Select(x=>x.Ratio);

or If you want the first one

var result = measurementUnits.Where(x=>x.Display.Contains("HEC")).First().Ratio;

Comments

0

This is the solution:

var measurementUnits = new [] {
    new { Unit = "SQF", Display = new List<string>() { "F", "FT", "SQ FT" }, Ratio=1.5 } ,
    new { Unit = "Hectares", Display = new List<string>() { "H", "HEC"} , Ratio=2.5},
    new { Unit = "Acres", Display = new List<string>() { "AC(TO)" } , Ratio=3.5},
    new { Unit = "SQM", Display = new List<string>() {  "M", "SQ M"}, Ratio=4.5 }
};
var multiplier = measurementUnits.Where(m => m.Display.IndexOf("H") > -1).FirstOrDefault()?.Ratio;

Comments

0

You cand do this all with FirstOrDefault:

 var multiplier = measurementUnits.FirstOrDefault(x => x.Display.Contains("H"))?.Ratio

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.