Find string in a List, if match, string = List name - c#

So my gameobject string name contain an ID(first variable) in the List, since the List is a custom class with two variable. I want to switch the name gameobject name to the second
variable after comparing the first variable and returning True.
if (gameData_List.showing_my_loading_list.Any(s => anchor.name.Contains(s.guid.ToString())))
{
Debug.Log("Found)
}
The result show 4 found, but what I'm trying to do is after knowing that is true, change the anchor.name to s.SecondVarible.Tostring();
However, after I set the condition in the if,
I no longer have access to the (s) in the Debug.Log area.

I managed to solve it myself, thanks
var temp = gameData_List.showing_my_loading_list.Find(x => anchor.name.Contains(x.guid.ToString()));
if (temp != null)
{
anchor.name = temp.readable_guid;
Debug.Log("Changing Name");
}

If you do not want to split it into lines you could probably do something like
anchor.name = gameData_List.showing_my_loading_list.FirstOrDefault(s => anchor.name.Contains(s.guid.ToString()))?.readable_guid ?? anchor.name;
So this does
FirstOrDefault: Try to find an element s where the condition is matched or return default (= null for classes)
?.: If it is not null use the readable_guid, otherwise return null again
??: If the before is null use anchor.name (the old one) as fallback => change nothing
Of course this loses the ability to do more things when you found a valid element like your logging etc.
Alternatively, why not simply stick to a normal loop ;)
foreach(var s in gameData_List.showing_my_loading_list)
{
if(anchor.name.Contains(s.guid.ToString())
{
anchor.name = s.readable_guid;
Debug.Log("Changing Name");
break;
}
}
or with Linq again
foreach (var s in gameData_List.showing_my_loading_list.Where(s => anchor.name.Contains(s.guid.ToString())))
{
anchor.name = s.readable_guid;
Debug.Log("Changing Name");
break;
}

gameData_List.showing_my_loading_list.filter(s => anchor.name.Contains(s.guid.ToString())).each(() => {
Debug.Log("Found)
})

Related

Generic method with dynamically selected parameter in predicate

I have many objects of diffrent type and i need to check few diffrent properties from each of them in the same way. I want to use this method inside object initializer like this:
collection.GroupBy(x => x.Identifier)
.Select(group => new SomeType
{
Identifier = group.Key,
Quantity = group.Sum(i => i.Quantity),
Type = MY_METHOD_HERE(group),
Name = MY_METHOD_HERE(group)
})
Strongly typed example for one of the properties:
private ItemType CheckItemTypeConsistency(IGrouping<string, Item> group)
{
if (group.Any(x => x.ItemType != group.First().ItemType))
{
throw new ArgumentException($"Item with number {group.Key} is inconsistent", nameof(Item.ItemType));
}
else
{
return group.First().ItemType;
}
}
But i also have other property in Item that needs to be checked in the same way with diffrent type, so i have similar method but .ItemType is changed everywhere to .Name and return type is string.
I also have diffrent object type that i need to use it for, so in another method Item is changed to Vehicle.
How to create generic method like that?
I tried something like this:
private TElement CheckConsistency<TKey, TElement>(IGrouping<TKey, TElement> group, (maybe something here?))
{
if (group.Any(x => x.(what here?) != group.First().(what here?)))
{
throw new ArgumentException($"Element with number {group.Key} is inconsistent");
}
else
{
return group.First();
}
}
I solved problem with returning value by returning whole item so i can just CheckConsistency().Property when invoking this method.
But i dont know what to do with (what here?).
I thought that maybe i can put something in (maybe something here?) that would be somehow used in place of (what here?)?
Any ideas? I'm not sure about reflection because this method could be called easily more than 1000 times depending on collection size and number of unique entries.
#Edit:
Lets say it's something like merging data from two files. For example 2 data sets of items where quantity is added together for items with the same identifier etc. but some properties should be the same like name and if they are diffrent then there's something wrong and i need to throw an error.
There are diffrent sets of data with completly diffrent properties like vehicles, but rules are similar, some fields are just added together etc, some must be identical.
Using an accessor function and genericizing the property type and object type, you have:
private TProp CheckConsistency<TClass, TProp>(IGrouping<string, TClass> group, Func<TClass, TProp> propFn) {
var firstPropValue = propFn(group.First());
if (group.Any(x => firstPropValue == null ? propFn(x) == null : !propFn(x).Equals(firstPropValue))) {
throw new ArgumentException($"Item with number {group.Key} is inconsistent");
}
else {
return firstPropValue;
}
}
Which you can use like:
var ans = collection.GroupBy(x => x.Identifier)
.Select(group => new SomeType {
Identifier = group.Key,
Quantity = group.Sum(i => i.Quantity),
Type = CheckConsistency(group, x => x.ItemType),
Name = CheckConsistency(group, x => x.Name)
});
If it is important to include the proper argument name in the exception, you could pass it in, take in an Expression<Func<>> and pull out the name, then compile the argument to a lambda to use (may be slow), or use reflection instead of a lambda property accessor (also possibly slow).
To use Reflection, I recommend caching the compiled function so you don't constantly re-compile each time the method is called:
// [Expression] => [Func]
Dictionary<LambdaExpression, Delegate> propFnCache = new Dictionary<LambdaExpression, Delegate>();
private TProp CheckConsistency<TClass, TProp>(IGrouping<string, TClass> group, Expression<Func<TClass, TProp>> propExpr) {
Func<TClass,TProp> propFn;
if (propFnCache.TryGetValue(propExpr, out var propDel))
propFn = (Func<TClass, TProp>)propDel;
else {
propFn = propExpr.Compile();
propFnCache.Add(propExpr, propFn);
}
var firstPropValue = propFn(group.First());
if (group.Any(x => !propFn(x).Equals(firstPropValue))) {
throw new ArgumentException($"Item with number {group.Key} is inconsistent", ((MemberExpression)propExpr.Body).Member.Name);
}
else {
return firstPropValue;
}
}

Check if string already exist in Table

I want to check if the email that user entered already exist in Office table,take a look bellow what I have done so far, the problem is that officeEmail is always true, even though the entered email doesn't exist it's never returning NULL.
public static bool IsOfficeEmail(string email)
{
using (var data = Database)
{
data.ObjectTrackingEnabled = false;
var officeEmail = data.Offices.Where(a => a.Active && a.Email.Equals(email));
if (officeEmail != null)
return true;
}
return false;
}
Where will not return you null, but empty sequence, change it to:
var officeEmail = data.Offices.FirstOrDefault(a => a.Active && a.Email.Equals(email));
if (officeEmail != null)
return true;
FirstOrDefault will return default value (here null) it will not find queried value.
It is an option to use Any if you are not interested in email record:
public static bool IsOfficeEmail(string email)
{
using (var data = Database)
{
return data.Offices.Any(a => a.Active && a.Email.Equals(email))
}
}
You will not get email record if you will not use it anyway. Which approach you should use depends on what will you do with officeEmail, if you are just querying if it exists -> Any will be the best approach here. If you would like to get check for existing record and do something with it, FirstOrDefault will be better.
Alternativly if you really want to use .Where you can also check if the returned collection contains ANY elements:
if (officeMail.Any()) // ...
Or even shorter:
if (officeMail.Any(a => a.Active && a.Email.Equals(email))) // ...
If you want to ensure that there is exactly ONE item within your list matching your condition use .Single instead of .Any which will throw an exception if none or more then one item was found.
The actual reason for your check allways returning true is that .Where will return an enumerator even when no item matches the condition. Therefor the result is never null.

If Element does not exist

I have around a dozen solutions to this, but none seem to fit what I am trying to do. The XML file has elements that may not be in the file each time it is posted.
The trick is, the query is dependent upon a question value to get the answer value. Here is the code:
string otherphone = (
from e in contact.Descendants("DataElement")
where e.Element("QuestionName").Value == "other_phone"
select (string)e.Element("Answer").Value
).FirstOrDefault();
otherphone = (!String.IsNullOrEmpty(otherphone)) ? otherphone.Replace("'", "''") : null;
Under the "contact" collection, here are many elements named "DataElement", each with its own "QuestionName" and "Answer" elements, so I query to find the one where the element's QuestionName value is "other_phone", then I get the Answer value. Of course I will need to do this for each value I am seeking.
How can I code this to ignore the DataElement containing QuestionName with value of "other_phone" if it doesn't exist?
You can use Any method to check whether or not the elements exists :
if(contact.Descendants("DataElement")
.Any(e => (string)e.Element("QuestionName") == "other_phone"))
{
var otherPhone = (string)contact
.Descendants("DataElement")
.First(e => (string)e.Element("QuestionName") == "other_phone")
.Element("Answer");
}
Also, don't use Value property if you are using explicit cast.The point of explicit cast is avoid the possible exception if the element wasn't found.If you use both then before the cast, accessing the Value property will throw the exception.
Alternatively, you can also just use the FirstOrDefault method without Any, and perform a null-check:
var element = contact
.Descendants("DataElement")
.FirstOrDefault(e => (string)e.Element("QuestionName") == "other_phone");
if(element != null)
{
var otherPhone = (string)element.Element("Answer");
}
So you want to know if other_phone exists or not?
XElement otherPhone = contact.Descendants("QuestionName")
.FirstOrDefault(qn => ((string)qn) == "other_phone");
if (otherPhone == null)
{
// No question with "other_phone"
}
else
{
string answer = (string)otherPhone.Parent.Element("Answer");
}

Passing object to linq result

I get an item from the list to manuplate it.
App.CurrentQuestion = App.AllQuestionList[dataSourceRowIndex];
After manuplations I save new data to database and get back from database into App.CurrentQuestion, so App.CurrentQuestion's list reference breaks. I want to update list and trying to focus selected item by linq
App.AllQuestionList
.Where(q => q.qID == App.CurrentQuestion.qID)
.FirstOrDefault() = App.CurrentQuestion;
but I get an error like "The left-hand side of an assignment must be a variable, property or indexer"
I can use this method
for (int i = 0; i < App.AllQuestionList.Count; i++)
{
if (App.AllQuestionList[i].qID == App.CurrentQuestion.qID)
{
App.AllQuestionList[i] = App.CurrentQuestion;
break;
}
}
but looking for an alternative method. Or is there any faster method?
You shouldn't have to do anything, since it's by reference.
App.CurrentQuestion = App.AllQuestionList[dataSourceRowIndex];
Whatever change you make in App.CurrentQuestion should be reflected in the App.AllQuestionList
App.AllQuestionList[App.AllQuestionList.IndexOf(App.AllQuestionList.
.Where(q => q.qID == App.CurrentQuestion.qID)
.First())] = App.CurrentQuestion;
Edit: you can just use the IndexOf to find the index of the object you wanted to find by LINQ query

c# copied property loses reference when referenced object is removed from list

Example
Have a look at the following code:
private void DeDuplicateOrganisations()
{
var profileOrgs = _organisations.Where(o => o.ExistsInProfile).ToList();
var kvkOrgs = _organisations.Where(o => !o.ExistsInProfile).ToList();
profileOrgs.ForEach(o =>
{
var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
if (duplicate != null)
{
o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
o.ExistsInBoth = true;
kvkOrgs.Remove(duplicate);
}
});
_organisations = profileOrgs.Concat(kvkOrgs).OrderBy(o => o.Title).ToList();
}
In this example the property CompanyInfoOrganisation (simply a get; set; property) is copied when an organisation is considered a duplicate. This all works as expected, duplicates are nicely deduplicated.
Also this is true inside this message:
_organisations.First(o => o.ExistsInBoth).CompanyInfoOrganisation != null;
Problem
Now I bind the _organisations list to a listbox
lbxCompanies.DataSource = null;
lbxCompanies.DataSource = _organisations;
lbxCompanies.DisplayMember = "Title";
lbxCompanies.SelectedIndex = -1;
and later on get the selected value:
var org = lbxCompanies.SelectedValue as Organisation;
gbxCompanyInfo.Visible = org != null;
if (gbxCompanyInfo.Visible)
if (org.CompanyInfoOrganisation != null)
// NEVER GETS HERE (but gbxComanpyInfo is visible)
If I try to read the CompanyInfoOrganisation property I always get null while I know the property was set.
Question
What is happening here? How come the property reference is destroyed? How can I prevent this from happening?
The reference you're using only has immediate scope and as soon as the query ends it exits scope and your reference disappears. So when you bind later, the reference is exactly right -- null.
profileOrgs.ForEach(o =>
{
// Right here -- var duplicate has scope ONLY within your query.
// As soon as the query is executed it leaves scope and the reference
// pointer will be null
var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
if (duplicate != null)
{
o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
o.ExistsInBoth = true;
kvkOrgs.Remove(duplicate);
}
});
Because you're using a class, you need to perform a deep MemberwiseClone on it to get a NEW copy of the object:
o.CompanyInfoOrganisation = (YourInfoType)duplicate.CompanyInfoOrganisation.MemberwiseClone();
When you load the data, load the CompanyInfoOrganisation property along with the root entity; that way it will be already loaded into memory. If using LINQ to SQL, you load via DataLoadOptions, and pass this to the context. If using Entity Framework, you use the Include method in the LINQ query.
It might have to do with capturing of variables inside the lambda. Try substituting the .ForEach to a regular foreach().
Or maybe the CompanyInfoOrganisation in duplicate was null to begin with.
The problem was I used string.Join() to show the values, and the first value to join was null (which is really annoying), resulting in an empty string, leaving me thinking the property was null. However it turned out the property was not null, but has a perfectly valid reference to the object needed. Using the debugger with a little more care would have saved me an hour or so...
Sorry!

Categories

Resources