Search for an object list - c#

I have a class like this.
class man
{
public string name { get; set; }
public string mail { get; set; }
}
I have a list like this
List<man> ppl = new List<man>();
I want to search for a man with name "Nimal" in the list and delete that man. How to do that?

How about List.RemoveAll Method
Removes all the elements that match the conditions defined by the
specified predicate.
arts.RemoveAll(x => x.name == "Nimal");

if you want to delete the first found in list:
if(arts.Any(x => x.name == "Nimal"))
{
arts.Remove(arts.FirstOrDefault(x => x.name == "Nimal"));
}
if you want to delete all entries with matching condition then,you can do this way:
if(arts.Any(x => x.name == "Nimal"))
{
arts.RemoveAll(x => x.name == "Nimal");
}

var Filtered= ppl.FirstOrDefault(a => a.Name == "Nimal");
var finalresult= ppl.Except(Filtered).ToList();

If you a want the first occurrence of the name otherwise checkout #astander answer:
var firstMatch = ppl.First (p => p.Name == "Nimal");
ppl.Remove (firstMatch);
If you prefer for loop and if-condition you can do something like that:
for (int i = 0; i < ppl.Count; i++)
{
man m = ppl[i] as man;
if(m.Name.ToLower() == "nimal")
ppl.Remove(m);
}

Related

C# Remove line of data from List

Here is my List declaration:
public List<CharactersOnline> charactersOnline = new List<CharactersOnline>();
public class CharactersOnline
{
public int connectionId;
public int characterId;
public string characterName;
}
Here is how i add lines into this list:
charactersOnline.Add(new CharactersOnline() { connectionId = cnnId, characterId = charId, characterName = name });
Here is how i think i can remove one line by just poiting one of the parameters:
private void CharacterLogout(int charId)
{
charactersOnline.Remove(new CharactersOnline() { characterId = charId });
}
Can i remove all the data on the line of the list by just pointing only one of the parameters or this is just going to delete the data for characterId only ?
I need to know how can i delete the whole line of this list.
One way to do that is to find it and then remove it:
var charactersOnline = new List<CharactersOnline>();
var target = charactersOnline.SingleOrDefault(x => x.characterId == 1);
charactersOnline.Remove(target);
So your method will be like this:
private void CharacterLogout(int charId)
{
var target = charactersOnline.SingleOrDefault(x => x.characterId == charId);
charactersOnline.Remove(target);
}
Another approach in more functional way.
private void CharacterLogout(int charId)
{
charactersOnline = charactersOnline.Where(line => line.characterId != charId)
.ToList();
}
Notice, that approach above will remove all lines with given charId value.
Your approach will not work because Remove method will search for element with same reference as given item to remove.
The line new CharactersOnline() { characterId = charId } will create new instance of object which reference not exists in the list.
So for removing correct item you should find it first.
var indexesToRemove =
charactersOnline.Select((line, index) => (Id: line.characterId, Index: index))
.Where(item => item.Id == charId)
.Select(item => item.Index);
foreach (var index in indexesToRemove)
{
charactersOnline.RemoveAt(index);
}
Use this code to remove item from List based on condition
charactersOnline.Remove(charactersOnline.Single(s => s.characterId = charId) );

What would be the LINQ solution for this query in c#?

I have a class RuleDetail:
public class RuleDetail
{
public int RuleDetailId;
public int StartYear;
}
I have a List of objects of type RuleDetail:
RuleDetailId=1, StartYear=0
RuleDetailId=2, StartYear=2
RuleDetailId=3, StartYear=4
RuleDetailId=4, StartYear=10
RuleDetailId=5, StartYear=13
RuleDetailId=6, StartYear=18
I will be given a number say x (x always >= 0); for that I need to find the RuleDetail object in the above List which matches these conditions:
Get the RuleDetail object where x equals to StartYear OR
Get the RuleDetail object of the max(StartYear) when StartYear < x
Assuming I have these variables
RuleDetail[] ruleDetails = null;
int x = -1;
// ruleDetails populated
// x populated
This is the code I have come up with:
bool found = false;
RuleDetail ruleDetail = null;
RuleDetail oldRuleDetail = null;
for (int i=0; i<ruleDetails.Length; i++)
{
if (ruleDetails[i].StartYear == x)
{
found = true;
ruleDetail = ruleDetails[i];
break;
}
else if (ruleDetails[i].StartYear > x)
{
found = true;
ruleDetail = oldRuleDetail;
break;
}
oldRuleDetail = ruleDetails[i];
}
if (!found)
{
ruleDetail = oldRuleDetail;
}
return ruleDetail;
The code is working ok. But how can I do this in LINQ?
Thanks
var output = ruleDetails.OrderBy(rule => rule.StartYear).Where(rule => rule.StartYear <= x).Last()
If the list is already in StartYear order then ....
var output = ruleDetails.Where(rule => rule.StartYear <= x).Last()
You can use
ruleDetails.FirstOrDefault(rd => rd.StartYear == x)
?? ruleDetails.Where(rd => rd.StartYear < x).OrderByDescending(rd => rd.StartYear).First();
which is a clear separation of your two requirements, but it is actually more concise to use
ruleDetails.Where(rd => rd.StartYear <= x).OrderByDescending(rd => rd.StartYear).First()
var res1 = (from a in ruleDetails where a.StartYear == x select a).First();
var res2 = (from a in ruleDetails orderby a.StartYear where a.StartYear < x select a).Last();
Here's a real simple LINQ snippet to accomplish what you're trying to do here. I'm writing this with the assumption that your list is ordered as that's what your current code suggests.
We'll first filter the list down to entries that are either the target year or less than it, then take the highest remaining element.
var filteredList = ruledetails.Where(r => r.StartYear <= targetYear);
return filteredList.Last;
It's kinda gross, but maybe something like:
var result = ruleDetails
.OrderBy(r => r.StartYear)
.FirstOrDefault(r => r.StartYear == x || r.StartYear == ruleDetails.Select(y => y.StartYear).Max());
(A) if the list is is initially sorted by StartYear, then
var result = ruleDetails.LastOrDefault(r => r.StartYear <= startYear);
(B) if the list is not sorted, then
var result = ruleDetails.Where(r => r.StartYear <= startYear)
.Aggregate((RuleDetail)null, (a, b) => a == null || a.StartYear < b.StartYear ? b : a);

How To: Use LINQ to search data for a string containing many possible types of "Single Quote"

The Problem:
My customers want to be able to search for first and last names in my site, using any version of ', `, or those goofy microsoft 'start' and 'end' quotes.
I would like to be able to write a single statement that looks for all possible permutations of that evil symbol, like such:
var customers = _sms.CurrentSession.Query<customer>()
.Where(c => c.FirstName.Contains(firstName)
&& c.LastName.Contains(lastName)
&& !c.IsEmployee)
.ToList();
and if the user was searching for "O'Brien", I would show them results for "O'Brien", and "O`Brien", and any other version of that stupid thing.
What's the best way to do that? Is there a way to do it (without a massive recursion where I do things like
if (firstName.Contains("'")
{
var customers = _sms.CurrentSession.Query<customer>()
.Where(c => c.FirstName.Contains(firstName)
&& c.LastName.Contains(lastName)
&& !c.IsEmployee)
.ToList();
firstName = firstName.Replace("'", "`");
customers.AddRange(_sms.CurrentSession.Query<customer>()
.Where(c => c.FirstName.Contains(firstName)
&& c.LastName.Contains(lastName)
&& !c.IsEmployee)
.ToList());
}
etc...
Update
private static readonly string[] Quotes = { "#", "'", "‘", "’", "`" };
private IEnumerable<customer> CustomersByFirstName(string firstName, bool showInactive = true)
{
firstName = firstName.Replace(Quotes[1], Quotes[0]);
var customers = _sms.CurrentSession.Query<customer>()
.Where(c => (c.FirstName.Replace(Quotes[1], Quotes[0]).Contains(firstName) ||
c.FirstName.Replace(Quotes[2], Quotes[0]).Contains(firstName) ||
c.FirstName.Replace(Quotes[3], Quotes[0]).Contains(firstName) ||
c.FirstName.Replace(Quotes[4], Quotes[0]).Contains(firstName))
&& !c.IsEmployee)
.ToList();
if (!showInactive)
{
customers = customers.Where(c => !c.Disabled).ToList();
}
return customers;
}
This is almost it... Almost
Final Update
This turns out to be exactly the solution, tho not as elegant and small as I might like:
private IEnumerable<customer> CustomersByFirstName(string firstName, bool showInactive = true)
{
firstName = firstName.Replace(Quotes[1], Quotes[0]);
firstName = firstName.Replace(Quotes[2], Quotes[0]);
firstName = firstName.Replace(Quotes[3], Quotes[0]);
firstName = firstName.Replace(Quotes[4], Quotes[0]);
var customers = _sms.CurrentSession.Query<customer>()
.Where(c => (c.FirstName.Replace(Quotes[1], Quotes[0]).Contains(firstName) ||
c.FirstName.Replace(Quotes[2], Quotes[0]).Contains(firstName) ||
c.FirstName.Replace(Quotes[3], Quotes[0]).Contains(firstName) ||
c.FirstName.Replace(Quotes[4], Quotes[0]).Contains(firstName))
&& !c.IsEmployee)
.ToList();
if (!showInactive)
{
customers = customers.Where(c => !c.Disabled).ToList();
}
return customers;
}
Sorry, I posted an answer without fully understanding the question. Here's the corrected answer.
I would define your criteria for a match in a separate function. Here's how I implemented it:
// Add quote characters as needed.
List<char> quotes = new List<char> { '\'', '‘', '’' };
public string CleanName(string name)
{
StringBuilder cleanName = new StringBuilder(name);
foreach (char quote in quotes)
{
cleanName = cleanName.Replace(quote, '\'');
}
return cleanName.ToString();
}
public bool IsMatch(string n0, string n1)
{
return String.Compare(CleanName(n0), CleanName(n1)) == 0;
}
Then, search for matching names using the IsMatch function, as demonstrated in this unit test:
[TestMethod]
public void TestQuoteSearch()
{
string searchName = "O'Brien";
var matchingNames = names.Where(name => IsMatch(name, searchName));
Assert.AreEqual(4, matchingNames.Count());
Assert.IsTrue(matchingNames.Contains("O'Brien"));
Assert.IsTrue(matchingNames.Contains("O‘Brien"));
Assert.IsTrue(matchingNames.Contains("O’Brien"));
Assert.IsFalse(matchingNames.Contains("Jones"));
}
You could do something like :
char[] all_quotes= new char[] { '\'', '‘', '’' /* any other quote you like */ };
Then replace them in your names with a special char (lets say *), or cut them out all together, and then search for those matches then.
have a look at this answer for an example of how to do it, or brew your own:
var customers = _sms.CurrentSession.Query<customer>()
.Where(c => c.FirstName.Replace(all_quotes, "*")
.Contains(firstName.Replace(all_quotes, "*"))
&& // same for others)
.ToList();
Regarding Final Update:
I wouldn't have the one to replace as part as the quotes. I'd do something like :
string inplace = "#";
private static readonly string[] Quotes = {"'", "‘", "’", "`" };
foreach (var q in Quotes)
firstName = firstName.Replace(q, inplace);
you can wrap the foreach in { } if you'd like. It's shorter, and if you end up adding a quote to the array, you don't need another line of "replace"
Create Store Procedure and import to Entity Modal then call like method.

Select single object from a query

I have the following query that pulls the data I need perfectly fine.
var subFuncName = from a in m_dcSQL_ConnectionProdTest.DC3_SubFunctions
where a.VersionIndex == versionIndex && stepDistinct.Select(b => b.Step).Contains(a.FunctionNumber) && stepDistinct.Select(c => c.LogID).Contains(a.SubFunctionNumber)
select new
{
a.FunctionNumber,
a.SubFunctionNumber,
a.SubFunctionName,
};
Then I want to add some data to a list.
foreach (var item in stepDistinct)
{
lstPareto.Add(new clsPareto(Convert.ToInt32(item.Step), Convert.ToInt32(item.LogID),
stepLogID.Where(p => p.Step.Equals(item.Step) && p.LogID.Equals(item.LogID)).Count(),
subFuncName.Where(x => x.FunctionNumber.Equals(item.Step) && x.SubFunctionNumber.Equals(item.LogID)).Select(x => x.SubFunctionName).ToString())); --THIS LINE IS THE PROBLEM--
}
My clsPareto class:
public class clsPareto
{
public int intStep { get; set; }
public int intLogID { get; set; }
public int iCount { get; set; }
public string strFuncName { get; set; }
public clsPareto(int ParetoStep, int ParetoLogID, int Count, string FuncName)
{
intStep = ParetoStep;
intLogID = ParetoLogID;
iCount = Count;
strFuncName = FuncName;
}
}
What I am trying to do, is to pull each SubFunctionName from subFuncName where FunctionNumber = Step and SubFunctionNumber = LogID. However, when I bind it to my datagrid, the column meant to show the names just shows the SQL Query String instead and doesn't actually take the elements I want. I thought my .Select(x => x.SubFunctionName) would do the trick, but apparently it doesn't. Still pretty new to using LINQ and C#, so how would I go about doing this?
The linq for the problem line is still an expression - Select() returns an IEnumerable not the value - and then you are doing a ToString() on it. This is why you just get SQL back.
You need to resolve the expression and get an actual object out of it. Adding Single() to get the FuncName should do it. You may also not need to convert to string if FuncName already is:
subFuncName.Where(x => x.FunctionNumber.Equals(item.Step) && x.SubFunctionNumber.Equals(item.LogID))
.Select(x => x.SubFunctionName).Single().ToString()));
This solution will throw an exception if there is no matching element in the collection.
subFuncName.Where(x => x.FunctionNumber.Equals(item.Step) && x.SubFunctionNumber.Equals(item.LogID))
.Select(x => x.SubFunctionName).Single().ToString()));
Could use;
var subFuncName = subFuncName.Where(x => x.FunctionNumber.Equals(item.Step) && x.SubFunctionNumber.Equals(item.LogID))
.Select(x => x.SubFunctionName).FirstOrDefault());
if(subFuncName != null)
// Add it
else
subFuncName == "UNDEFINED";
and handle the case if subFuncName is null yourself.

How to update a single item of liist of objects using LINQ in C#

I want like to update the Value of the list which has property Text="ALL".
public class Season
{
public string Text {get;set;}
public string Value {get;set;}
public bool ValueSelected {get;set;}
}
The 'Q' in LINQ stands for "Query". LINQ is not meant to update objects.
You can use LINQ to find the object you want to update and then update it "traditionally".
var toUpdate = _seasons.Single(x => x.Text == "ALL");
toUpdate.ValueSelected = true;
This code assumes that there is exactly one entry with Text == "ALL". This code will throw an exception if there is none or if there are multiple.
If there is either none or one, use SingleOrDefault:
var toUpdate = _seasons.SingleOrDefault(x => x.Text == "ALL");
if(toUpdate != null)
toUpdate.ValueSelected = true;
If it's possible that there are multiple, use Where:
var toUpdate = _seasons.Where(x => x.Text == "ALL");
foreach(var item in toUpdate)
item.ValueSelected = true;
You could use something like this:
// Initialize test list.
List<Season> seasons = new List<Season>();
seasons.Add(new Season()
{
Text = "All"
});
seasons.Add(new Season()
{
Text = "1"
});
seasons.Add(new Season()
{
Text = "2"
});
seasons.Add(new Season()
{
Text = "All"
});
// Get all season with Text set to "All".
List<Season> allSeasons = seasons.Where(se => se.Text == "All").ToList();
// Change all values of the selected seasons to "Changed".
allSeasons.ForEach(se => se.Value = "Changed");

Categories