Sujoy
Sujoy

Reputation: 1207

C# LINQ query on anonymous types containing dynamic list

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;

Upvotes: 1

Views: 2856

Answers (6)

Aleks Andreev
Aleks Andreev

Reputation: 7054

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();    

Upvotes: 4

Calin Vlasin
Calin Vlasin

Reputation: 1433

You cand do this all with FirstOrDefault:

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

Upvotes: 0

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

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 */;

Upvotes: 1

David
David

Reputation: 641

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;

Upvotes: 1

Marco Salerno
Marco Salerno

Reputation: 5203

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;

Upvotes: 0

Anu Viswan
Anu Viswan

Reputation: 18155

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;

Upvotes: 0

Related Questions