I have 2 lists and I would like to remove the items when the items from the first list is not present in the second list.
public class ResolutionsRow
{
public String Name { get; set; }
public int ID { get; set; }
}
public List<ResolutionsRow> Categories { get; set; }
In the following Category.LoadForProject(project.ID) returns an IList
DeleteItems(Category.LoadForProject(project.ID), Categories);
private void DeleteItems(dynamic currentItems, dynamic items)
{
if (currentItems != null)
{
foreach (var existingItem in currentItems)
{
if (items.Contains(existingItem.Name))
items.Remove(existingItem.Name);
}
}
}
I am having the error message
The best overloaded method match for 'System.Collections.Generic.List.Contains(MvcUI.Models.ResolutionsRow)' has some invalid arguments. What is wrong with my code and how can I correct it? Help please.
I have tried to change the code to, but I am having the error message
Error 6 Argument 1: cannot convert from 'int' to API.Category' MvcUI\Models\ProjectModel.cs 255 44 MvcUI
Error 5 The best overloaded method match for 'System.Collections.Generic.ICollection.Contains(API.Category)' has some invalid arguments MvcUI\Models\ProjectModel.cs 255 24 MvcUI
var categories = Category.LoadForProject(project.ID);
foreach (var item in Categories)
{
if(categories.Contains(item.ID))
{
}
}
Here's the easy LINQ answer:
var currentItems = new int[] { 1, 2, 5, 6 };
var items = new int[] { 2, 3, 4, 5 };
var resultItems = items.Except(currentItems); // resultItems == new int[] { 3, 4 }
What is items? I'm guess it is the list of ResolutionsRow - so you will need to search for an item with that name/id, not the name/id itself.
If they are the same object instances, then just Remove(existingItem) will work, but otherwise (if they are different object instances that happen to have the same .Name):
items.RemoveAll(item => item.Name == existingItem.Name);
by the way; do you really need dynamic here? Without it, the compiler would tell you the problem. It isn't helping you any, and could well cause a lot of problems (explicit interface implementations, lambdas, etc - there are constructs that aren't fans of dynamic)
Change
items.Contains(existingItem.Name);
and
items.Remove(existingItem.Name);
to
items.Contains(existingItem);
items.Remove(existingItem);
you want to use items1.Except(items2)
Your items.Contains method signature is expecting a type different than what you provided. Looks like you are providing a string instead of a ResolutionsRow.
How often are you doing this, and with how many items in each list? What you are doing is commonly considered a "Set operation"(union, intersection, minus, etc). If the answer to either of the previous questions is "a lot", then you want to consider using a SortedSet or HashSet.
Your current implementation is O(m*n) (where m and n are the sizes of the two lists). If you use a hash set it is O(n), since only the second set is actually iterated over. There is also a cost to build the sets (O(m+n)), but if you have enough objects or can use it for more than just one operation it can be worth it.
Related
I am working with an XML standard called SDMX. It's fairly complicated but I'll make it as short as possible. I am receiving an object called CategoryScheme. This object can contain a number of Category, and each Category can contain more Category, and so on, the chain can be infinite. Every Category has an unique ID.
Usually each Category contains a lot of Categories. Together with this object I am receiving an Array, that contains the list of IDs that indicates where a specific Category is nested, and then I am receiving the ID of that category.
What I need to do is to create an object that maintains the hierarchy of the Category objects, but each Category must have only one child and that child has to be the one of the tree that leads to the specific Category.
So I had an idea, but in order to do this I should generate LINQ queries inside a cycle, and I have no clue how to do this. More information of what I wanted to try is commented inside the code
Let's go to the code:
public void RemoveCategory(ArtefactIdentity ArtIdentity, string CategoryID, string CategoryTree)
{
try
{
WSModel wsModel = new WSModel();
// Prepare Art Identity and Array
ArtIdentity.Version = ArtIdentity.Version.Replace("_", ".");
var CatTree = JArray.Parse(CategoryTree).Reverse();
// Get Category Scheme
ISdmxObjects SdmxObj = wsModel.GetCategoryScheme(ArtIdentity, false, false);
ICategorySchemeMutableObject CatSchemeObj = SdmxObj.CategorySchemes.FirstOrDefault().MutableInstance;
foreach (var Cat in CatTree)
{
// The cycle should work like this.
// At every iteration it must delete all the elements except the correct one
// and on the next iteration it must delete all the elements of the previously selected element
// At the end, I need to have the CatSchemeObj full of the all chains of categories.
// Iteration 1...
//CatSchemeObj.Items.ToList().RemoveAll(x => x.Id != Cat.ToString());
// Iteration 2...
//CatSchemeObj.Items.ToList().SingleOrDefault().Items.ToList().RemoveAll(x => x.Id != Cat.ToString());
// Iteration 3...
//CatSchemeObj.Items.ToList().SingleOrDefault().Items.ToList().SingleOrDefault().Items.ToList().RemoveAll(x => x.Id != Cat.ToString());
// Etc...
}
}
catch (Exception ex)
{
throw ex;
}
}
Thank you for your help.
So, as i already said in my comment, building a recursive function should fix the issue. If you're new to it, you can find some basic information about recursion in C# here.
The method could look something like this:
private void DeleteRecursively(int currentRecursionLevel, string[] catTree, ICategorySchemeMutableObject catSchemeObj)
{
catSchemeObj.Items.ToList().RemoveAll(x => x.Id != catTree[currentRecursionLevel].ToString());
var leftoverObject = catSchemeObj.Items.ToList().SingleOrDefault();
if(leftoverObject != null) DeleteRecursively(++currentRecursionLevel, catTree, leftoverObject);
}
Afterwards you can call this method in your main method, instead of the loop:
DeleteRecursively(0, CatTree, CatSchemeObject);
But as i also said, keep in mind, that calling the method in the loop, seems senseless to me, because you already cleared the tree, besides the one leftover path, so calling the method with the same tree, but another category, will result in an empty tree (in CatSchemeObject).
CAUTION! Another thing to mention i noticed right now: Calling to list on your Items property and afterwards deleting entries, will NOT affect your source object, as ToList is generating a new object. It IS keeping the referenced original objects, but a deletion only affects the list. So you must write back the resulting list to your Items property, or find a way to directly delete in the Items object. (Assuming it's an IEnumerable and not a concrete collection type you should write it back).
Just try it out with this simple example, and you will see that the original list is not modified.
IEnumerable<int> test = new List<int>() { 1, 2, 3, 4 , 1 };
test.ToList().RemoveAll(a => a != 1);
Edited:
So here is another possible way of going after the discussion below.
Not sure what do you really need so just try it out.
int counter = 0;
var list = CatSchemeObj.Items.ToList();
//check before you call it or you will get an error
if(!list.Equals(default(list)))
{
while(true)
{
var temp = list.Where(x => CatTree[counter++] == x.Id); // or != ? play with it .
list = temp.Items.ToList().SingleOrDefault();
if(list.Equals(default(list))
{
break;
}
}
}
I just translated you problem to 2 solutions, but I am not sure if you won't lose data because of the SingleOrDefault call. It means 'Grab the first item regardless of everything'. I know you said you have only 1 Item that is ok, but still... :)
Let me know in comment if this worked for you or not.
//solution 1
// inside of this loop check each child list if empty or not
foreach (var Cat in CatTree)
{
var list = CatSchemeObj.Items.ToList();
//check before you call it or you will get an error
if(!list.Equals(default(list)))
{
while(true)
{
list.RemoveAll(x => x.Id != Cat.ToString());
list = list.ToList().SingleOrDefault();
if(list.Equals(default(list))
{
break;
}
}
}
}
//solution 2
foreach (var Cat in CatTree)
{
var list = CatSchemeObj.Items.ToList();
//check before you call it or you will get an error
if(!list.Equals(default(list)))
{
CleanTheCat(cat, list);
}
}
//use this recursive function outside of loop because it will cat itself
void CleanTheCat(string cat, List<typeof(ICategorySchemeMutableObject.Items) /*Place here whatever type you have*/> CatSchemeObj)
{
CatSchemeObj.RemoveAll(x => x.Id != cat);
var catObj = CatSchemeObj.Items.ToList().SingleOrDefault();
if (!catObj.Equals(default(catObj)){
CleanTheCat(cat, catObj);
}
}
Thank you to whoever tried to help but I solved it by myself in a much easier way.
I just sent the full CategoryScheme object to the method that converted it in the XML format, then just one line did the trick:
XmlDocument.Descendants("Category").Where(x => !CatList.Contains(x.Attribute("id").Value)).RemoveIfExists();
I have made a list in C# and I want to make a test to see if all the values of the Id fields are unique.
public static List<RestaurantReview> _reviews = new List<RestaurantReview>
{
new RestaurantReview
{
Id = 1,
Name = "McDonalds",
},
new RestaurantReview
{
Id = 2,
Name = "Burger King",
},
}
Because I did some debugging I fount out that it is running trough the list but I do not get the proper test value. Could someone please explain what I am doing wrong here?
[TestMethod()]
public void CheckReviewIds()
{
var FirstReview = ReviewsController._reviews.First();
bool AllUniqueIds = ReviewsController._reviews.All(s => s.Id == FirstReview.Id);
Assert.IsFalse(AllUniqueIds);
}
Thanks in advance.
Another solution that will have better performance in terms of time (specially if the data set that you have is large) is to use a HashSet like this:
bool IsAllUnique<T>(IEnumerable<T> values)
{
HashSet<T> hash_set = new HashSet<T>();
return values.All(x => hash_set.Add(x));
}
And then you can use it like this:
bool unique = IsAllUnique(ReviewsController._reviews.Select(x => x.Id));
This solution depends on the fact that HashSet.Add will return false if the value we are trying to add already exists.
One reason why this solution has better performance is that it does not have to go through the rest of the items in the list if it detects a duplicate item.
You're checking that all values are not equal to the first. This can be the case if the values are for example [1, 2, 3, 3], none are equal to the first but itself, but 3 == 3.
Instead, you can GroupBy to group them by value and then check that they are distinct. I'm assuming performance isn't a big issue here (that's the case if the list is less than 100000 items which I assume):
ReviewsController._reviews.GroupBy(x => x.Id).Count() == ReviewsController._reviews.Count;
Note that it might not be the best idea to test the internal state of a component, instead test the API it is exposing. Otherwise, the contract you define through your unit tests is limited by your implementation detail. This last paragraph is just on man's opinion though.
I find that the easiest way to verify this is to count all the distinct values and compare them with the number of actual values:
var actual = ReviewsController._reviews.Select(r => r.Id).Distinct().Count();
var expected = ReviewsController._reviews.Count();
Assert.Equal(expected, actual);
How is it possible to find a specific object from a list?
Lets say i have a function that takes an object and a list that contains objects of this type and returns the number at which position the specific object is found.
The only way i could think of a solution is to run the list through with a foreach loop, but isn't there a better way?
Thanks
You can use the IndexOf(T item) method:
myList.IndexOf(myItem);
It returns the index of the first occurrence of the item.
The only way i could think of a solution is to run the list through with a foreach loop
Generally, you need a loop (a for or foreach) to find an object in a list. You could use it directly, or through a function that iterates over list elements, but there is going to be a loop. There is no way around it: unless you know something special about the way the elements of the array are arranged, you have to look at them all.
One case of knowing something special about arrangement of elements is knowing that an array is sorted. If this is the case, and when you know the value of the attribute on which the element is sorted, you can find the element much faster by using binary search.
You could use linq expressions
List.where(condition)
Or
List.Select(condition).FirstOrDefault
Based on search condition it will return the item you want.
You can use method IndexOf or if you use a special condition then you can use method
public int FindIndex(Predicate<T> match);
where match is delegate
public delegate bool Predicate<T>(T obj);
In fact it is similar to standard C++ algorithm std::find_if
To see whether object is there You might just need List<T>.Contains method
It states,
Determines whether an element is in the List.
And you need to use it like List<T>.Contains(T type item) , where T is the same type of List and item you need to compare. In your case it's a the type of Object
And to return the index you can use List<T>.IndexOf Method
Searches for the specified object and returns the zero-based index of the first occurrence within the entire List.
Simple Console program
class Program
{
static void Main(string[] args)
{
MyType a = new MyType() { id = 10 };
MyType b = new MyType() { id = 20 };
MyType c = new MyType() { id = 30 };
List<MyType> testList = new List<MyType>();
testList.Add(a);
testList.Add(b);
Console.WriteLine(testList.Contains(a)); // <= Will return true
Console.WriteLine(testList.Contains(c)); // <= Will return false
Console.WriteLine(testList.IndexOf(a)); // <= will return 0 : the index of object a
Console.ReadKey();
}
}
// A simple class
class MyType
{
private int ID;
public int id
{
get { return ID; }
set { ID = value; }
}
}
Good day all,
I have a class and a property, and I have three instances of that class.
public class myclass {
public int myproperty;
}
...
myclass inst1, inst2, inst3;
...
Now at a certain point I need to compare those three property values, and verify that they be equal or not, to end up with the least amount of values.
So if I have
inst1.myproperty = 3;
inst2.myproperty = 5;
inst3.myproperty = 3;
after the magic_function_call, I should get 3 and 5.
And if I have
inst1.myproperty = 2;
inst2.myproperty = 2;
inst3.myproperty = 2;
after the magic_function_call, I should get 2.
Albeit this is trivial per se, and can be solved with as many IF checks as needed, I was wondering which is the fastest, or more efficient way to do it, especially in light of the fact that I might need to add another variable to the check in the future.
I am in fact wondering if there is a bitwise operation that can be performed that can solve this elegantly and quickly.
Alternatively, is there an array operation that can be used to achieve the same goal? I've tried looking for 'reduction' or 'compression' but those keywords don't seem to lead in the right direction.
Thanks in advance.
You can use the morelinq DistinctBy query operator if all of the instances belong to a collection:
List<MyClass> myList = new List<MyClass>();
.. populate list
List<MyClass> distinct = myList.DistinctBy(mc => mc.myproperty).ToList();
Looking at the question, you may want a list of just the property values (a list of ints), which you can achieve with the standard query operators:
List<int> distinct = myList.Select(mc => mc.myproperty).Distinct().ToList();
Note that you haven't defined a property, you've defined a public field. To define an auto property change:
public int myproperty;
to
public int myproperty { get; set; }
Note also that PascalCasing is recommended for property and class names.
Here's a quick function which doesn't require any extra libraries and avoids the setup costs and overheads associated with LINQ:
static int[] Reduce(IEnumerable<myclass> items)
{
HashSet<int> uniqueValues = new HashSet<int>();
foreach (var item in items)
{
uniqueValues.Add(item.myproperty);
}
return uniqueValues.ToArray();
}
Pass it a collection of your myclass instances and it will return an array of unique myproperty values.
Just anohter way to implement it .
var result = myList.GroupBy(p => p.myproperty).Select(p => p.First());
I wanted to generate a unique identifier for the results of a Linq query i did on some date.
Initially i thought of using Guid for that but stumbling upon this problem i had to improvise.
However I'd like to see if anyone could have a solution using Guid so here we go.
Imagine we have:
class Query
{
public class Entry
{
public string Id { get; set; }
public int Value { get; set; }
}
public static IEnumerable<Entry> GetEntries( IEnumerable<int> list)
{
var result =
from i in list
select new Entry
{
Id = System.Guid.NewGuid().ToString("N"),
Value = i
};
return result;
}
}
Now we want Id to be unique for each entry, but we need this value to be the same for each traversal of the IEnumerable we get from GetEntries. This means that we want calling the following code:
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
IEnumerable<Query.Entry> entries = Query.GetEntries(list);
Console.WriteLine("first pass");
foreach (var e in entries) { Console.WriteLine("{0} {1}", e.Value, e.Id); }
Console.WriteLine("second pass");
foreach (var e in entries) { Console.WriteLine("{0} {1}", e.Value, e.Id); }
to give us something like:
first pass
1 47f4a21a037c4ac98a336903ca9df15b
2 f339409bde22487e921e9063e016b717
3 8f41e0da06d84a58a61226a05e12e519
4 013cddf287da46cc919bab224eae9ee0
5 6df157da4e404b3a8309a55de8a95740
second pass
1 47f4a21a037c4ac98a336903ca9df15b
2 f339409bde22487e921e9063e016b717
3 8f41e0da06d84a58a61226a05e12e519
4 013cddf287da46cc919bab224eae9ee0
5 6df157da4e404b3a8309a55de8a95740
However we get:
first pass
1 47f4a21a037c4ac98a336903ca9df15b
2 f339409bde22487e921e9063e016b717
3 8f41e0da06d84a58a61226a05e12e519
4 013cddf287da46cc919bab224eae9ee0
5 6df157da4e404b3a8309a55de8a95740
second pass
1 a9433568e75f4f209c688962ee4da577
2 2d643f4b58b946ba9d02b7ba81064274
3 2ffbcca569fb450b9a8a38872a9fce5f
4 04000e5dfad340c1887ede0119faa16b
5 73a11e06e087408fbe1909f509f08d03
Now taking a second look at my code above I realized where my error was:
The assignment of Id to Guid.NewGuid().ToString("N") gets called every time we traverse the collection and thus is different everytime.
So what should i do then?
Is there a way i can reassure that i will get with only one copy of the collection everytime?
Is there a way that i'm sure that i won't be getting the new instances of the result of the query?
Thank you for your time in advance :)
This is a inherent to all LINQ queries. Being repeatable is coincidental, not guaranteed.
You can solve it with a .ToList() , like:
IEnumerable<Query.Entry> entries = Query.GetEntries(list).ToList();
Or better, move the .ToList() inside GetEntries()
Perhaps you need to produce the list of entries once, and return the same list each time in GetEntries.
Edit:
Ah no, you get each time the different list! Well, then it depends on what you want to get. If you want to get the same Id for each specific Value, maybe in different lists, you need to cache Ids: you should have a Dictionary<int, Guid> where you'll store the already allocated GUIDs. If you want your GUIDs be unique for each source list, you would perhaps need to cache the input the return IEnumerables, and always check if this input list was already returned or not.
Edit:
If you don't want to share the same GUIDs for different runs of GetEntries, you should just "materialize" the query (replacing return result; with return result.ToList();, for example), as it was suggested in the comment to your question.
Otherwise the query will run each time you traverse your list. This is what is called lazy evaluation. The lazy evaluation is usually not a problem, but in your case it leads to recalculating the GUID each query run (i.e., each loop over the result sequence).
Any reason you have to use LINQ? The following seems to work for me:
public static IEnumerable<Entry> GetEntries(IEnumerable<int> list)
{
List<Entry> results = new List<Entry>();
foreach (int i in list)
{
results.Add(new Entry() { Id = Guid.NewGuid().ToString("N"), Value = i });
}
return results;
}
That's because of the way linq works. When you return just the linq query, it is executed every time you enumerate over it. Therefore, for each list item Guid.NewGuid will be executed as many times as you enumerate over the query.
Try adding an item to the list after you iterated once over the query and you will see, that when iterating a second time, the just added list item will be also in the result set. That's because the linq query holds an instance of your list and not an independent copy.
To get always the same result, return an array or list instead of the linq query, so change the return line of the GetEntries method to something like that:
return result.ToArray();
This forces immediate execution, which also happens only once.
Best Regards,
Oliver Hanappi
You might think not using Guid, at least not with "new".
Using GetHashCode() returns unique values that don't change when you traverse the list multiple times.
The problem is that your list is IEnumerable<int>, so the hash code of each item coincides with its value.
You should re-evaluate your approach and use a different strategy. One thing that comes into my mind is to use a pseudo-random number generator initialized with the hash code of the collection. It will return you always the same numbers as soon as it's initialized with the same value. But, again, forget Guid
One suggestion: (Don't know if that's your case or not though)
If you want to save the entries in database, Try to assign your entry's primary key a Guid at the database level. This way, each entry will have a unique and persisted Guid as its primary key. Checkout this link for more info.