I have the following class:
public static IEnumerable<SelectListItem> GetDatastore()
{
return new[]
{
new SelectListItem { Value = "DEV", Text = "Development" },
new SelectListItem { Value = "DC1", Text = "Production" },
};
}
What I need is to execute a function to return the Datastore name. Something like
var abc = getDatastoreName("DEV").
Do I need to do this with LINQ or is there some easy way? How could I code this function?
public static string getDatastoreName(string name)
{
var result = GetDatastore().SingleOrDefault(s => s.Value == name);
if (result != null)
{
return result.Text;
}
throw /* some exception */
}
The Value property of SelectListItem is usually unique and hence I have SingleOrDefault(). If that is not the case then you can switch to using FirstOrDefault().
A simple LINQ query can find the value you want:
var val = dataStore.Where(d => d.Value == "DEV").FirstOrDefault();
//`val` will be the item, or null if the item doesn't exist in the list
But this is only good for small lists of items -- it's worst-case Order N.
If you wanted a better search, you could store your data as a dictionary with the keys being used as item values, for example, and databind against that rather than against a list of SelectListItems. That would allow you to look up the values constant time.
For most cases, simple LINQ will be fine. If you have a big list, or you're querying that list frequently... consider an alternative.
Maybe you are searching something like this
i have "Details" page that works perfectly:
#Html.DisplayFor(model => model.Code1dItems.SingleOrDefault(m => m.Value == model.Code1Id.ToString()).Text, new { #class = "Width100P" })
In my model :
Code1Id is int value and it comes from database Code1dItems is IEnumerable
value like 'GetDatastore' and returns ID->string, ID value matches with Code1d and Text->string
depending on your question you should use :
string abc = GetDatastore.SingleOrDefault(m => m.Value == "DEV").Text
if you get value from database you should use my code example.
Related
I have a collection and I would like to filter it with one of the column contains multiple values. The filter values are dynamically generated and I dont know how many I will get.
I tried the following without success:
var input = #"was.Name.Contains(""Test"") || was.Name.Contains(""Test2"")";
var test = collection.Where(was => input)).ToList();
Assuming you receive the filter values as a CSV string:
var csvFilters = "Test1, Test2";
// split by ',', remove empty entries,
// trim each filter and store the result in a list
var filters = csvFilters.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
// return items in collection whose Name property
// is equal to any of the items in filters
var result = collection.Where(x => filters.Contains(x.Name)).ToList();
This should translate to the following SQL:
SELECT * FROM collection c
WHERE c.Name IN ('Test1', 'Test2')
I guess you want to use LINQ. The question is, how the "filter" values are kept? I'll answer in the way I understand your question.
If input is supposed to be a condition then I'd suggest using Func<Object,bool>. This means, the input would be the condition you're looking for, and if found, it would return true.
Here is a simple example:
IEnumerable <T> FindElements (Func<Object, bool> condition, IEnumerable<T> inputList)
{
List<T> outputList = new List<T>();
foreach(var element in inputList)
{
if(condition != null && condition(element))
outputList.Add(element);
}
return outputList;
}
Then, if you call the function given exemplary parameters:
string input[] = {"Test1","Test2"};
foreach(string s in input)
{
targetList = FindElements(element=>((cast)element).Name.Contains(s), collection);
}
You should get all elements in collection which name has Test1 or Test2. Cast is of course name of the class which element instantiates.
I have a SelectList and I need to filter out when the Text value is emptyOrNull? I'm struggling to get this working. Any ideas? I've tried this
Example code
// My list - this is valid code
SelectList list = model.MySelectList;
// But when I try this I get the following error message?
SelectList test = list.Where(x => x.Text != "");
I receive the following compiler error message
Error
Error convert source .. IEnumerable<SelectListItem> to target type SelectList
UPDATE - I need to filter out values, i.e. I need something like
foreach (var item in list)
{
if (string.IsNullOrEmpty(item.Text))
{
// remove item from list
}
}
// Then list does not include any items with a Value which is null or empty
UPDATE
After making the following change, the code compiles but I get 'Select.Web.Mvc.SelectListItem' in the rendered dropdown.
new SelectList(list.Where(x => x.Text != ""));
Please advise, many thanks,
You can do in two ways,
(i). Getting only the values which are not null or not empty using LINQ
List<SelectListItem> test = list.Where(item => item.Text != null || !(item.Text.Equals(string.Empty))).ToList();
(ii).Second way is to find the values and remove from the original
List<SelectListItem> filedNullOrEmpty = list.Where(item => item.Text == null || item.Text.Equals(string.Empty)).ToList();
foreach (Select selobj in filedNullOrEmpty)
{
list.Remove(selobj);
}
It is happened because .Where() returns IEnumerable. You should convert to SeletList type.
One of option is .Where(x => x.Text != "").Select(new SelectListItem(){Text = x.Text, Value = x.Value});
The SelectList constructor takes an IEnumerable so all you need to do is pass the LINQ query to the constructor like so
var query = from c in source
where c.Key != ""
select c;
var customerList = new SelectList(query, "DataTextField", "DataValueField");
Wish I could help you.
Enumerable.Where returns IEnumerable<T>, not SelectList - try using this constructor:
SelectList test = new SelectList(list.Where(x => !String.IsNullOrEmpty(x.Text)));
I have a large List<> of Name/Value pairs and need to be able to find if a particular Name exist in this list...if so update that Value, if NOT then create a new Name/Value pair and append it to my List.
What is the Lambda syntax to do this?
I have tried using .Where .DefaultIfEmpty but it just returns an empty object but does not append it to my original 'myList'
myList.Where(x => x.Name == 'some name').DefaultIfEmpty(new myobj(){ Name = 'some name').First().Value = 'some value';
I have to do this four multiple Names so if I had a simple syntax that would make it great =)
You can use this extension method:
public static void UpdateOrAdd(this List<myobj> source,
string name, object value)
{
var obj = source.FirstOrDefault(x => x.Name == name);
if (obj == null)
{
obj = new myobj { Name = name };
source.Add(obj);
}
obj.Value = value;
}
Usage:
myList.UpdateOrAdd("some name", "some value");
But consider to use dictionary, as #Sam suggested.
Your actual query is just getting the first item that meets a condition. LINQ is for querying not for modifying a collection, so you shouldn't be adding the item to the collection as a part of the query; do that as a part of processing the query.
var match = list.FirstOrDefault(x => x.Name == "some name");
if(match != null)
match.Value = "something else";
else
list.Add(new MyObject(){...});
If you want a more generic GetOrAdd method you can make one as well:
public static T GetOrAdd<T>(this IList<T> list, Func<T, bool> predicate,
Func<T> constructor)
{
var match = list.FirstOrDefault(predicate);
if(match == null)
{
match = constructor();
list.Add(match);
}
return match;
}
Using that you could write something like:
var item = list.GetOrAdd(obj => obj.Name == "Some Name", () => new MyObj(){...});
item.Value = "Some Value";
Having done that, please strongly consider using a Dictionary for this collection rather than a List of pairs as it can be much more efficiently searched based on the key.
If you did have a Dictionary then the code to do this would be as simple as:
dictionary["some name"] = "some value";
And in addition to being shorter in code, it will perform dramatically faster.
It just so happens that I just wrote this class for my own project, using a Dictionary:
public class CachedLoader<K, T> : Dictionary<K, T>
{
private Func<K,T> GetItem = null;
public CachedLoader(Func<K,T> getItem)
{
this.GetItem = getItem;
}
public new T this[K key]
{
get
{
if (!base.ContainsKey(key)) base[key] = GetItem(key);
return base[key];
}
}
}
The constructor takes a lambda expression to load the data. You might use it like this:
CachedLoader<Guid, Employee> MOEmployees
= new CachedLoader<Guid, Employee>(id => EmployeeManager.List(id));
And you would consume it like this:
Guid employeeId = ...;
MOEmployees[employeeId]
There is no need to explicitly create/load the item from the code that you are consuming the data from because the class handles it for you - you just have to specify how to do it in the constructor. You would adapt the constructor to create the value however you need it.
If you are stuck on a List, then you could easily adapt this to that structure as well.
DefaultIfEmpty does not append to lists. It returns the specified value if the collection passed to it is empty. So in your case, nothing will happen because you're not assigning the result to anything; just allowing it to be discarded.
There's not a simple lambda chain to do what you want. You'll have to do two steps. First, you can see if there are any results that match what you're looking for, then append if not.
var defaultObj = new myobj() { Name = "default name", Value = "default value"};
if(!list.Any(x => x.Name == "some name")){
list.Add(defaultObj);
}
Edit: in response to comment, I did miss updating the original. You could do something like this:
var obj = list.FirstOrDefault(x => x.Name == "some name");
if(obj == null){
obj = new myobj() { Name = "default name" };
list.Add(obj);
}
obj.Value = "some value";
Use Any()
if(list.Any(x => x.Name == "some name"))
{
// Replace
}
else
{
// Add
}
I have the following code populate for dropdownlist. Is there any way i can add an item called "Select Value" in the following
private void PopulateCustStatus()
{
ViewData["custStatus"] = new HEntities().COptions
.Select(e => new ConfOptModel
{
OptID = e.OptID,
OptName = e.OptName,
ConfigTypeID = e.ConfigTypeID
})
.Where(e => e.ConfigTypeID == 2)
.OrderBy(e => e.OpName);
}
The problem with what you're trying to do, I think, is that your "no selection" option (e.g. "Select Value") doesn't have the same "shape" as the model being created in PopulateCustStatus().
You probably want to use an IEnumerable<SelectListItem>:-
var selectList =
new HEntities().COptions
.Where(e => e.ConfigTypeID == 2)
.OrderBy(e => e.OpName)
.Select(e => new SelectListItem()
{
Text = e.OptName,
Value = e.OptID,
Selected = false
});
This gives you a little bit more flexibility. Then you can insert a "Select value" item on top like:-
var noSelection = new SelectListItem()
{
Text = "Select value",
Value = 0,
Selected = true
};
ViewData["selectedCustStatus"] =
new[] { noSelection }.Concat(selectList);
Note: If you want to preserve a previously selected value, you'll have to pass it into the function and make sure the matching SelectListItem gets its Selected property set to true.
Hope this helps!
You're returning a list of ConfOptModel object so unless there's a property to indicate a "selected" value then you can't.
If I don't create models specifically for the View (i.e. use domain objects as the "Model" instead of a "ViewModel") I usually add the selected item to the view state :
ViewData["selectedCustStatus"] = selectedID;
And then use that value when creating the DropDownList in the markup.
Another option is to return a list of SelectListItem objects instead of domain (ConfOptModel) objects. That puts a little of the view logic in the controller so use whichever you're more comfortable with.
I have an IQueryable I selected from a database that contains a field called "NewValue". This field will either contain a 0, 1, or 2. I want to replace the value with a string based of which number it is.
i.e 0 = "Active". I know IQueryables aren't used to change values mostly just for querying but I need to use it in the next line to add to the ViewModel
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
newValue = p.NewValue,
});
How can I change all the values in assement.NewValue to a string?
var dictionary = new Dictionary<int, string>
{
{ 0, "Active" },
{ 1, "Inactive" },
// etc
};
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
newValue = dictionary[p.NewValue],
});
This would work for mapping between other types as well.
var values = new [] {"StrValue1", "StrValue2", "StrValue"};
var AssessmentList = assessment
.AsEnumerable()
.Select(p => new LogManagementViewModel
{
newValue = values[p.NewValue],
});
Easiest and cleanest would be with a mapping method:
string MapToString(int value)
{
//..
}
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
NewValue = MapToString(p.NewValue),
});
One way:
Add a new computed property onto your class (assuming you have a partial class outside of any generated code). Then that computed class can do the translation however it needs to. Your view model can reference this computed value.
I would make a constructor overload in your LogManagementViewModel that does that work for you, something like this:
public LogManagementViewModel(int NewValue)
{
switch(NewValue)
{
case 0:
this.NewValue = "Active";
break;
// etc...
}
}
IMO, this places the logic in it's proper place, let the your view model translate it as it should be, it's not the responsibility of your LINQ/Database queries.