I ran into some problem in Unity, How am I supposed to remove an item from a List by its ID or name? I think this should work, but evidentally it doesn't.
....
List<MyDataType> myList = new List<MyDataType>();
....
public static void removeItemFromList(int id)
{
foreach (MyDataType item in myList)
{
if (item.TypeId == id)
Debug.Log("List contains ID: " + item.TypeId);
}
// PRINTS AN OBJECT WITH THE ID - works as expected
var match = myList.Find(p => p.TypeId == id);
// SEEMS LIKE IT CAN'T FIND THE OBJECT WITH THE ID
if (match == null)
{
Debug.Log("DOES NOT EXIST");
return;
}
// always returns DOES NOT EXIST, WHY?
myList.Remove(match);
}
I don't have unity in front of me,
but you could try myList.Select(item => item.TypeId == id).SingleOrDefault();
or as Fabjan states:
myList.SingleOrDefault(item => item.TypeId == id);
Jasper is correct, but make sure that you add the LINQ library to your project or you won't have access to FirstOrDefault on a list. That may be why you couldn't find the definition for it.
using System.Linq;
Related
This seems like it should be easy. Maybe it is and I'm just overthinking it. I have a bunch of items that have a category field set via a DropLink. I want to grab all of the items that match one of those options. E.g., Grab a list of all items where Category=Brochure. I can't seem to get the ID of the Droplink option to match against the Category option on the Item itself.
EDIT: Included current code by request.
public List<PoolDownload> Manuals
{
get
{
LookupField cat = (LookupField)this.Item.Fields["Category"];
return this.Downloads.Where(i => (i.Item.TemplateID == PoolDownload.TemplateId) &&
(i.Item.GlassCast<Pdp.Pool.Website.Business.Entities.PoolDownload>().Category.ToString() == cat.TargetID.ToString()))
.ToList();
}
}
I believe the problem is you're comparing a Guid.ToString() to a Sitecore.Data.ID.ToString(). These two statements return different values:
var guidToString = Sitecore.Context.Item.ID.Guid.ToString();
// "2a6a1d9a-be1d-411b-821a-7e63775280b3"
var idToString = Sitecore.Context.Item.ID.ToString();
// "{2A6A1D9A-BE1D-411B-821A-7E63775280B3}"
Cast the TargetID to a Guid as well and you should be good.
And to answer your question in your comment below about displaying the "Download Items" grouped by Category, you could use the GroupBy method, https://msdn.microsoft.com/en-us/library/bb534304(v=vs.110).aspx like this:
public IEnumerable<IGrouping<Guid, PoolDownload>> Manuals
{
get
{
LookupField cat = (LookupField)this.Item.Fields["Category"];
return this.Downloads.Where(i =>
i.Item.TemplateID == PoolDownload.TemplateId
&& i.Item.GlassCast<Pdp.Pool.Website.Business.Entities.PoolDownload>().Category.ToString() == cat.TargetID.Guid.ToString())
.GroupBy(i => i.Category);
}
}
And then, to loop over the results in the new Manuals property, you could do something like this:
foreach(var categoryGroup in Manuals)
{
var categoryGuid = categoryGroup.Key;
foreach(var download in categoryGroup)
{
var downloadInCurrentGroup = download.Item;
}
}
So i have a function that gets a list of students from a web service and also query the localdb for all the students in there. the data is placed in two different list. So i want to check to see if a new student already exists in the localdb List. if it does, update it and it if doesn't then add it. i unable to get it working . I am trying to perform this using LINQ, but i can't seem to get it working right. My LINQ skills are amateurish at best.
public async Task GetStudents()
{
String controllerName = "Students";
List<Students> newStudentData = await RunGetAsync<Students>(controllerName);
// get all the service types that already exists in the localStudent Db
List<Students> currentStudentData = db.Studentss.ToList();
foreach (Students existingStudents in currentStudentData)
{
foreach (Students newStudents in newStudentData)
{
IEnumerable<Students> selectStudents = from student in newStudentData // check if Students exist in the database
where student.Id == existingStudents.Id
select student;
if (selectStudents == null) // didn't find it, then add it
{
db.Students.Add(newStudents);
}
if (selectStudents != null) // found it , then update the informations
{
Students updatedStudents = new Students();
foreach (var field in selectStudents)
{
updatedStudents.FName = field.FName;
updatedStudents.LName = field.LName;
updatedStudents.ZipCode = field.ZipCode;
updatedStudents.AccessCode = field.AccessCode;
}
db.Entry(updatedStudents).State = System.Data.Entity.EntityState.Modified;
}
}
}
db.SaveChanges();
}
Thank you very much for your help.
you're looping more than you need :
foreach (Students newStudents in newStudentData)
{
var student = currentStudentData.FirstOrDefault(s => s.Id == newStudents.Id);
if(student == null)
{
//add
}
else
{
//update
}
}
with FirstOrDefault you can find out if it exists and get a reference to it at the same time, if it does.
You could use Intersect and Except like below:
//Find students that already exist to update
var updateStudents = currentStudentData.Intersect(newStudentData);
//Find new students to add
var addStudents = newStudentData.Except(currentStudentData);
I filled some ObservableCollection<Employe> collection:
// Program.Data.Employees - it is ObservableCollection<Employe>.
Program.Data.Employees.Add(new Employe() { Name="Roman", Patronymic="Petrovich", Surname="Ivanov" });
Program.Data.Employees.Add(new Employe() { Name = "Oleg", Patronymic = "Vladimirovich", Surname = "Trofimov" });
Program.Data.Employees.Add(new Employe() { Name = "Anton", Patronymic = "Igorevich", Surname = "Kuznetcov" });
In other place of my code I try to remove some item from this collection:
// Program.Data.Employees - it is ObservableCollection<Employe>.
Employe x = Program.Data.Employees.First(n => n.Guid == emp.Guid); // x is not null.
Int32 index = Program.Data.Employees.IndexOf(x); // I got -1. Why?
Boolean result = Program.Data.Employees.Remove(x); // I got 'false', and item is not removed. Why?
// But this works fine:
Program.Data.Employees.Clear();
I can clear collection, but I can't remove necessary item. Why it happens?
UPD: Equals method of my Employe class
public bool Equals(Employe other) {
return
other.Guid == this.Guid &&
String.Equals(other.Name, this.Name, StringComparison.CurrentCultureIgnoreCase) &&
String.Equals(other.Patronymic == this.Patronymic, StringComparison.CurrentCultureIgnoreCase) &&
String.Equals(other.Surname == this.Surname, StringComparison.CurrentCultureIgnoreCase) &&
other.Sex == this.Sex &&
String.Equals(other.Post == this.Post, StringComparison.CurrentCultureIgnoreCase);
}
I tried the following code to reproduce your error:
class Employee
{
public string Name { get; set; }
public Guid Guid { get; set; }
}
// ...
ObservableCollection<Employee> employees = new ObservableCollection<Employee>();
var guid1 = Guid.NewGuid();
employees.Add(new Employee { Name = "Roman", Guid = guid1 });
employees.Add(new Employee { Name = "Oleg", Guid = Guid.NewGuid() });
var x = employees.First(e => e.Guid == guid1);
var index = employees.IndexOf(x); // index = 0, as expected
var result = employees.Remove(x); // result = true, as expected
It worked as expected. I would suggest, you set a breakpont at var x = ... and check, if
The collection really contains the item you're looking
If First() really returns that item
Then go to the next line and check, if index is returned correctly. And finally check again, if result is really false.
I see several possible causes of your code failing:
You didn't post the full code and something happens between x=Program.Data.Employees.First() and Program.Data.Employees.IndexOf()
You use multithreaded code (which also results in "something happening" between the two statements). In this case, you need to synchronize the access to the collection
You don't use a ObservableCollection directly but some derived class instead which is constructed by your data layer (such as DataServiceCollection, but this one should work fine too). In this case, check the actual type of your collection in the debugger
Another typical cause of errors with collection would be, if you try to remove items while iterating over the collection (i.e. inside a foreachloop): but in this case an exception should be thrown (and IndexOf should work fine), so this would only apply if you use some derived class which implements non-standard behaviour.
EDIT (in return to you posting your Equal method)
Your Equal method has a serious error in it:
String.Equals(other.Patronymic == this.Patronymic, StringComparison.CurrentCultureIgnoreCase)
... // applies also for following comparisons
should be
String.Equals(other.Patronymic, this.Patronymic, StringComparison.CurrentCultureIgnoreCase)
...
Also, if you're using a Guid, consider only comparing the GUIDs, since this usually means 'unique identifier', so it should be enough to identify some entity.
I need confirmation of my approach for this, I'm using EF and ASP.NET MVC and I'm trying to remove entities based on user selection (i.e based on what they have checked/unchecked).
To do this I'm looking at the Ids that are passed from the form from the checkboxes, matching what I have in the database and then first adding any which are new and then removing any which don't match.
Following is the code that I originally had:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection, VMinstanceRole vmodel)
{
try
{
var instancerole = db.instanceRoles.Find(id);
if (ModelState.IsValid)
{
UpdateModel<instanceRole>(instancerole, "instanceRole");
var keys = instancerole.rights.Select( c => c.Id);
foreach (var pid in vmodel.selectedId.Except(keys))
{
var right = new right { Id = pid };
db.rights.Attach(right);
instancerole.rights.Add(right);
}
foreach (var pid in keys.Except(vmodel.selectedId))
{
var right = instancerole.rights.Where(c => c.Id == pid).Single();
instancerole.rights.Remove(right);
}
db.SaveChanges();
}
// TODO: Add update logic here
return RedirectToAction("Index");
}
catch (InvalidCastException e)
{
return View();
}
}
However, the following error was presented "Collection was modified; enumeration operation may not execute."
So to try and resolve this I decided to keep a seperate list and remove it based on teh list afterwards to overcome the error:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection, VMinstanceRole vmodel)
{
try
{
var instancerole = db.instanceRoles.Find(id);
List<right> removeList = new List<right>();
if (ModelState.IsValid)
{
UpdateModel<instanceRole>(instancerole, "instanceRole");
var keys = instancerole.rights.Select( c => c.Id);
foreach (var pid in vmodel.selectedId.Except(keys))
{
var right = new right { Id = pid };
db.rights.Attach(right);
instancerole.rights.Add(right);
}
foreach (var pid in keys.Except(vmodel.selectedId))
{
var right = instancerole.rights.Where(c => c.Id == pid).Single();
removeList.Add(right);
}
foreach (var right in removeList)
{
instancerole.rights.Remove(right);
}
db.SaveChanges();
}
// TODO: Add update logic here
return RedirectToAction("Index");
}
catch (InvalidCastException e)
{
return View();
}
}
This seems to work, however, I'm not sure whether I've done the right thing. Mainly becuase I'm doing another loop. Is there a better way to approach this or is this good enough ?
You found one standard solution. The other solution that works would be to call ToList on the LINQ operation that produces your keys object: doing so would disconnect keys from instanceroles collection, allowing for arbitrary independent modifications on the original collection.
Try this:
foreach (var pid in keys.Except(vmodel.selectedId).ToList())
{
var right = instancerole.rights.Where(c => c.Id == pid).Single();
instancerole.rights.Remove(right);
}
Enumerator you enumerate in foreach loop will be already disposed by the moment you delete your first item.
The reason for not being able to edit a collection when enumerating with foreach is well enough documented here alone (just check the 'related' links to the side), and in knowing you can't do that, you could use a simple for loop and amend to index upon removal of an item - this allows you to maintain one loop.
for (int i = 0; i < max; i++) {
//if removing an item
//manipulate the index as desired...
i--;
}
I notice i do this pattern a lot. Is there a better way to write this?
bool hit=false;
foreach (var tag in tags)
if (tag == sz)
{
hit = true;
break;
}
if (hit) continue;
//tags.add(sz); or whatever i wanted to do
I know if sz in tags exist in other languages. I hope theres something in linq that can help?
For the example:
if (tags.Contains(sz)) ...
For the more general problem:
if (tags.Any(tag => InvolvedLogic(tag))) ...
Assuming tags is a List<T>:
if (tags.Contains(sz))
{
// ...
}
If you just want to know if a given item is in tags, do:
if(tags.Any(t => t == sz))
{
// Do stuff here
}
If you want to grab a reference to the found item, do:
var foundTag = tags.FirstOrDefault(t => t == sz);
// foundTag is either the first tag matching the predicate,
// or the default value of your tag type
if (tags.Any(t=>t == sz) == true)
{
//...
}