Find() method of list - c#

I have a login method.
My data is organized like this:
public class globalLists
{
public List<course> courseList = new List<course>();
public List<user> userList = new List<user>();
public List<string> registeredUsernameList = new List<string>();
I have a class with 3 lists that is static-instantiated
public class user
{
public int currentCourse = 0;
public course[] courseQ = new course[10];
public string username { get; set; }
public string name { get; set; }
public string lastName { get; set; }
public string code { get; set; }
public string password { get; set; }
public string email { get; set; }
Next is the logOn class, with the BLL Logon User method
public static class logOn
{
static BusinessEntities.user userAccess = new BusinessEntities.user();
public static bool BLLLogonUser(string username, string psw)
{
userAccess.username = username;
userAccess.password = psw;
int index = userlist.FindIndex(a => a.username == userAccess.password);
BusinessEntities.user pswss = user.Find(a => (a.password == userAccess.password) && (a.username == userAccess.username));
if (pswss.) // ignore this
{
return true;
}
else
{
return false;
}
}
I have 2 objects, the instantiated one called User Access, his attributes are username and password from BLLLogonUser() parameters.
AND the object pssws which actually was named hitting the keyboard,
My question is: pssws will search for a object on the list that meet the conditions given and take all the other values from found object?
BusinessEntities.user pswss = user.Find(a => (a.password == userAccess.password) && (a.username == userAccess.username));

My question is: pssws will search for a object on the list that meet the conditions given and take all the other values from found object?
No, especially since you are calling Find on the user object, when this is a property of List type. This will be a compilation error.
Modified Code (use the userList)
BusinessEntities.user pswss = userList.Find(a => (a.password == userAccess.password) && (a.username == userAccess.username));
This will fill the correct reference object from the List and since it is a reference so it will point to the object in the List, thus contain all the properties. Thus, any modification to the Object will change the object in the List (this is shallow copy not deep copy)
Better Option
Use System.Linq, Where and modify as follows:
BusinessEntities.user pswss = userList.FirstOrDefault(a => (a.password == userAccess.password) && (a.username == userAccess.username));
Edit 1: (Answer the OP question)
Where / FirstOrDefault was introduced in Linq, and applied on the all the collections derived from IEnumerable<T>, which means literally all the collection, thus wider in usage. Find is only List<T> method, which was introduced in .Net 2.0, thus precedes Linq.
In terms of performance, both of them will be O(N) linear search, so not much difference though Linq is convenient / self descriptive in usage. If the number of records are high and performance is impacted due to size of the collection, then consider converting List to Hashset for O(LogN) search or Dictionary, if the data is compatible (as it needs Unique Key) for quickest O(1) search

Related

Filter Out List with another list using LINQ

Hi I'm having difficulty finding an answer to my question here, so I figured I'd just ask. I have to lists of classes, ServiceItem, and ServiceDetailsClass. I want to filter out all of the ServiceDetailClass Items that are not int ServiceItems list. Here are the two classes:
public class ServiceItem
{
public long ID { get; set; }
public string Status { get; set; }
}
public class ServiceDetailsClass
{
public string Name;
public long ID;
public int Quantity;
public string Notes;
public string Status;
public string Description;
public DateTime CreatedDate;
}
So far the only things I've found on here is for lists that have a list in them, so this is a bit different. This is all I was able to come up with, but the filter list has 0 item, even though I know it should have more than that:
lstFilteredServiceDetailsClass = lstServiceDetailsClass.Where(i => lstServiceItem.Contains
(new ServiceItem { lngId = i.ServiceID, strStatus = "Locked" })
Any help is appreciated. Thanks!
You're making a new object and then checking the list to see if that exact object/instance is in it (i.e. because it's an object, it's comparing the reference).
Instead, you need to look for overlapping IDs.
Something like this should work:
List<ServiceItem> serviceItems;
List<ServiceItemDetails> serviceItemDetails;
var result = serviceItemDetails.Where(sid => serviceItems.Any(si => si.ID == sid.ID))
In English: "The collection of ServiceItemDetails where the list of service items has an item with the same ID"

How can I get the values and count from a model? [duplicate]

This question already has answers here:
C#: how to return a list of the names of all properties of an object?
(4 answers)
Closed 6 years ago.
I'd like to be able to get the values and count from any model I create.
For example let's say I have a model that looks like this.
public class test
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I want to be able to write code that will look at the model and then get ID, Name, Address and put them into an array. And I don't want the values. But the values from the model. Not the data. As well as getting the count of the values. 3.
EDIT: per your clarifications in the comments
You can use reflection to extract the property names into a list
var foo = new test();
IList<string> properties = foo.GetType().GetProperties()
.Select(p => p.Name).ToList();
OLD ANSWER
Try converting your object to a NameValueCollection (https://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection(v=vs.110).aspx). This collection offers a count, and allows you hash table access to the values. You can also retrieve an IEnumerable for the Values (or Keys) to suit your needs.
var foo = new test();
NameValueCollection formFields = new NameValueCollection();
foo.GetType().GetProperties()
.ToList()
.ForEach(pi => formFields.Add(pi.Name, pi.GetValue(foo, null).ToString()));
NOTE: if .ToString() is too destructive, you can swap the NameValueCollection with the IDictionary implementation of your choice.
Code modified from this question: how to convert an instance of an anonymous type to a NameValueCollection
In the other class simply call test.ID = identification;
identification being the variable that you would like ID to get the value from.
test being your class name.
I believe you are looking for something like this (wrapped in example code).
class Program
{
public class Test
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
static void Main(string[] args)
{
var propertyInfo = typeof(Test).GetProperties();
var propertyCount = propertyInfo.Count();
Console.WriteLine($"Property count is {propertyCount}");
foreach (var info in propertyInfo)
{
Console.WriteLine($"Property Name: {info.Name}");
}
Console.ReadKey();
}
}
Which would give you the output:
Code Output
This allows you to get the property count and property names of a class.

How to Cast List<T> To List<ClassName>

I am using generic method to fill my dropdown for all types
below is my code.
the entity type are as follow
public class Role
{
public string Id { get; set; }
public string Name { get; set; }
}
public class DropDown
{
public string Id { get; set; }
public string Name { get; set; }
}
i am able to fetch data successfully at
var data = DataFetcher.FetchData<T>();
private static void Main( string[] args )
{
List<DropDown> cities = BLL.GetDataList<City>();
List<DropDown> states = BLL.GetDataList<State>();
List<DropDown> roles = BLL.GetDataList<Role>();
}
public static class BLL
{
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data as List<DropDown>;
}
}
I knew this cast data as List<DropDown> will fail,thats why its returning null back to calling method,
How can i cast Generic list to List of Known Type?
You have to ask yourself: how do I want to convert T to DropDown? If you can't answer this, the answer is: you can't.
I guess your DropDown class has an object Value property, that holds the dropdown value, and you wish to assign the data entity to that property.
Then you can project the list of data entities to DropDowns as such:
var data = DataFetcher.FetchData<T>();
return data.Select(d => new DropDown { Value = d }).ToList();
As for your edit: so you have at least one type, the displayed Role, that has an Id and Name property. But type T doesn't guarantee this, so you'd need to introduce an interface:
public interface INamedIdentifyableEntity
{
string Id { get; set; }
string Name { get; set; }
}
And apply this to your entities. Then introduce it as a generic constraint and do the mapping:
return data.Select(d => new DropDown
{
Id = d.Id,
Name = d.Name,
}).ToList();
But you don't want this, as here you are tying these two properties to dropdowns. Tomorrow you'll want an entity with Code instead of Id and Text instead of Name, so you'll have to add more interfaces, more overloads, and so on.
Instead you might want to use reflection, where you can specify the member names in the call:
List<DropDown> cities = BLL.GetDataList<City>(valueMember: c => c.CityCode, displayMember: c => c.FullCityname);
And use these member expressions to look up data's values and fill those into the DropDown.
However, you're then reinventing the wheel. Leave out your DropDown class entirely, and leave the dropdown generation to the front end, in this case MVC:
var cities = DataFetcher.FetchData<City>();
var selectList = new SelectList(cities.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCityId),
Text = c.FullCityName,
Value = c.CityCode,
});
Or:
var selectList = new SelectList(cities, "CityCode" , "FullCityName", selectedCityId);
One solution is to use AutoMapper.
First create a map between your models like this:
AutoMapper.Mapper.CreateMap<Role, DropDown>();
Do the same thing for City and State classes if you need to.
Then you can use AutpMapper to convert your objects to DropDown like this:
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data.Select(x => AutoMapper.Mapper.Map<DropDown>(x)).ToList();
}
If I understood the question correctly, you could use Linq as follows.
return data.Cast<DropDown>().ToList();

Check to see if value exists in list - better way than a loop?

I have the following class data structure:
public class clsUser
{
public string userid { get; set; }
public List<OrgPermission> orgs { get; set; }
}
public class OrgPermission
{
public string Org { get; set; }
public string SubOrg {get;set;}
public List<string> type { get; set; }
}
List<string> type can have values such as "admin", "user", "superuser", etc.
so each user can have multiple org-suborg combinations with multiple user roles to each.
orgs and suborgs in the user class can be written out like so: 56% (which means they can see everything that starts with 56)
I want to check if the user has access to org-suborg combination on a page of type "Admin"
right now I am doing it with a loop, which works, like so:
foreach (OrgPermission userOrg in user.orgs) {
if ((ddlOrg.SelectedValue.StartsWith(userOrg.Org.Trim('%'))) && (ddlSubOrg.SelectedValue.StartsWith(userOrg.SubOrg.Trim('%')))) {
if (userOrg.type.Contains("Admin"))
btnSubmitToProd.Enabled = true;
else
btnSubmitToProd.Enabled = false;
break; //break out of the loop if the org-sub org match is found
}
}
is there a better way to do this to get rid of the loop maybe? or am I doing it right?
It sounds like you want:
string orgValue = ddlOrg.SelectedValue;
string subOrgValue = ddlSubOrg.SelectedValue;
btnSubmitToProd = user.orgs
.Any(org => orgValue.StartsWith(org.Org.Trim('%')) &&
subOrgValue.StartsWith(org.SubOrg.Trim('%')) &&
org.type.Contains("Admin"));
You can use Enumerable.Any:
var userIsAdmin = user.orgs.Any(uo => uo.type.Any(uot => uot == "Admin"));
I am not sure about using any inbuild method but i still go by your way , any code will at the end will use the foreach logic only , There is no magic way. Even using for in place of foreach will be much faster. I will still vote the way you are doing because it will give you more power in hand. Using any method like LINQ is fine but for loop is the best one.

comparing objects and returning list

I have a class definition that looks like this:
public class MyObjectModel
{
public int ObjectID { get; set; }
//for when the user's data is split in 2 fields
public string FirstName { get; set; }
public string LastName { get; set; }
//for when the user's data is all in one field
public string FirstLastName { get; set; }
}
I have a list of these MyObjectModel and I want to sort them by name with a custom sort process because that involves checking if the data contains a LastName (in this case sort using the LastName) or just FirstLastName (in this case I'm going to break the string and sort on the second term, if there's one, or just the whole string if there's only one word.)
I'm not sure on two things:
should I use IComparer or IComparable?
Once it determines the order of the sort (I can do that), how do I make it so that the output of the method is a list of ints representing ObjectID.
Use Linq:
List<MyObjectModel> objects = new List<MyObjectModel>();
List<int> ids = objects.OrderBy(o => FunctionWhichReturnsNameForSort(o)).Select(o => o.ObjectID).ToList();
FunctionWhichReturnsNameForSort can be implemented in another class, or an extension, or as a member.
// should be in a static helper class
public static string NameForSort(this MyObjectModel obj)
{
if (!string.IsNullOrEmpty(obj.LastName)) return obj.LastName;
return obj.FirstLastName.Split(... // your splitting logic goes here
}
var ids = objects.OrderBy(o => o.NameForSort()).Select(o => o.ObjectID).ToList();
When you really need this strange double solution then you will run into this and similar problems more often. As a more general solution, consider putting the business logic for names in a few read-only properties:
//for when the user's data is split in 2 fields
public string FirstName { get; set; }
public string LastName { get; set; }
//for when the user's data is all in one field
public string FirstLastName { get; set; }
public string FullName
{
get { ... } // pick from LastName, FirstName, FirstLastName
}
public string SortName
{
get { ... } // pick from LastName, FirstLastName
}
Once it determines the order of the sort (I can do that), how do I make it so that the output of the method is a list of ints representing ObjectID
result = MyObjectList
.OrderBy(m => m.SortName) // sort on SortName
.Select(m => m.ObjectID) // select the Id
.ToList();
If this sorting is specific to one use case, it can be achieved using LINQ:
var sortedIds = models.OrderBy(SecondName).Select(m => m.ObjectId).ToList();
private static string SecondName(MyObjectModel model)
{
if (!string.IsNullOrWhitespace(model.LastName)) return model.LastName;
return model.FirstLastName.Split(' ').Last();
}
While you can use LINQ, as others have suggested, that would involve creating a brand new list, not mutating the existing list. That may or may not be preferable. If you want to sort the list itself that's easy enough too:
List<string> list = new List<string>(){"a","b","c"};
list.Sort((a,b)=> a.CompareTo(b));
Just take your list, call Sort, and pass in a lambda that takes two items and returns an integer indicating which is greater. In your case, just call some method on a and b to get a string and then use CompareTo or string.Compare on those two strings.

Categories

Resources