I search in my list with already sorted data like this:
public class ShortWord: IComparable<ShortWord>
{
public int id { get; set; }
public string Word { get; set; }
public int CompareTo(ShortWord obj)
{
return this.Word.CompareTo(obj.Word);
}
}
List<ShortWord> words;
words.Where(t => t.Word.IndexOf(text.ToUpper()) == 0).Take(30).ToList();
It is working very slowly. I think need use List.BinarySearch but I don't understand how can I use it for my example.
I trying implement something but it isn't working.
Since compare is based on the word, you can create new instance with input word and pass it to the BinarySearch method:
List<ShortWord> words;
int index = words.BinarySearch(new ShortWord() {
Word = text,
};
if (index >= 0) {
ShortWord result = words[index];
}
Accoring to MSDN, BinarySearch will use the implemented IComparable.CompareTo method:
This method uses the default comparer Comparer.Default for type T
to determine the order of list elements. The Comparer.Default
property checks whether type T implements the IComparable generic
interface and uses that implementation, if available. If not,
Comparer.Default checks whether type T implements the IComparable
interface. If type T does not implement either interface,
Comparer.Default throws an InvalidOperationException.
Edit:
If you may have multiple items with the same word in the list, you should iterate the list from index until you get an item with different word.
Related
So if it is a dictionary, I can check if the Key or Value contains something on its own.
But if you have a list with a custom class. Things are different. In the script below, the dictionary is working properly since the wayspotAnchors.ID type is called Guid, and the key of a dictionary is Guid, so in Dictionary, it only compares if it contains the ID by using ContainKey.
However, when I use a List with a custom class. In the Custom class, it has more than one variable which allows my list to store many things.
The problem is when I compare the wayspotAnchors.ID with the List, it compares to everything that is stored in the list which creates an error because the second variable type in the custom list is not matching with the Guid only the first variable is. But there is no way to only compare the first variable of a custom List as Dictionary does.
private void CreateAnchorGameObjects(IWayspotAnchor[] wayspotAnchors)
{
foreach (var wayspotAnchor in wayspotAnchors)
{
if (gameData_List.my_second_game_list.)
{
}
if (_wayspotAnchorGameObjects.ContainsKey(wayspotAnchor.ID))
{
Debug.Log("working");
continue;
}
The custom class
public class MySecondGameList
{
public Guid guid;
public string readable_guid;
public GameObject anchor_gameobject;
}
List of the custom class
public class GameData_List : MonoBehaviour
{
public List<MySecondGameList> my_second_game_list;
}
The error
You need to iterate over the list applying the Any IEnumerable Extension. The Any method requires a lambda expression that returns a boolean value. If, during the enumeration, any lambda expression applied to the enumerated element, returns true, then the Any method stops the enumeration and returns true to your code
var searchedGuid = wayspotAnchor.ID;
if (gameData_List.my_second_game_list.Any(x => x.guid == searchedGuid))
{
// true
}
In stead of Contains you can use the Linq extension Any like this:
if (_wayspotAnchorGameObjects.Any(anchor => anchor.guid == wayspotAnchor.ID))
The Methode Array.Sort() has the following signature
public static void Sort (Array array, System.Collections.IComparer? comparer);
It looks like you need to pass an IComparer reference. But what is really needed is that array needs to implements IComparable, isn't it?
I see this syntax the first time. Is this common? How can I differentiate between a real parameter? Is there somewhere more information about this topic (in general)?
Important/Edit: ATM I'm reading a C# book and it says about Sort.Array (translated from German to English):
To the first parameter we pass the array to be sorted, in our case
arr. The second parameter is of type IComparer interface. Of course,
you can't pass an instance of type IComparer to the method call,
because interfaces are not instantiable. This is not how the type
specification of the second parameter should be understood. Instead,
the second parameter simply requires that the fist argument passed to
it be an object that implements the interface IComparer - whether the
object is of type DemoClass, Circle,
Basically he says that the second parameter is kind of a description for the first parameter. Is he correct or maybe that's just wrong and the source for my confusion?
https://openbook.rheinwerk-verlag.de/visual_csharp_2012/1997_04_008.html
I just implemented the following snippet. So this could be a way how to pass the second parameter, right?
Array.Sort(shapes, (a, b) => {
if (a.GetArea() < b.GetArea()) return -1;
else if (a.GetArea() > b.GetArea()) return 1;
return 0;
});
If you do not pass the comparer it will use the default comparer implementation for the Array items. But if you have a special comparer then you can pass your own custom Comparer to sort the elements.
Suppose you have a Class of Students (Array of Students), and your default Student comparer can be based on total marks. However, a maths teacher may want to sort the Students based on marks for the Maths only, in that case maths teacher can write his custom MathsRankComparer and pass it to the Sort method so that he will get the Students ordered by marks in Maths.
Similarly, English or Science teacher can pass the respective comparers to get their required ranking/ordering/sorting.
Hope this helps in understanding use of that overload.
Update: some examples to understand details.
public class Student: IComparable<Student>
{
public int ID { get; set; }
public string Name { get; set; }
public float TotalMarks { get; set; }
public float ScienceMarks { get; set; }
public float MathsMarks { get; set; }
public float EnglishMarks { get; set; }
public int CompareTo(Student other)
{
if (this.TotalMarks == other.TotalMarks)
return 0;
if (this.TotalMarks < other.TotalMarks)
return -1;
return 1;
}
}
public class MathsMarksBasedComparer : System.Collections.Generic.IComparer<Student>
{
public int Compare(Student a, Student b)
{
if (a.MathsMarks == b.MathsMarks)
return 0;
if (a.MathsMarks < b.MathsMarks)
return -1;
return 1;
}
}
public class EnglishMarksBasedComparer : System.Collections.Generic.IComparer<Student>
{
public int Compare(Student a, Student b)
{
if (a.EnglishMarks == b.EnglishMarks)
return 0;
if (a.EnglishMarks < b.EnglishMarks)
return -1;
return 1;
}
}
And finally, you can use them like this.
Student[] arr = new Student[100]; // Ignore this, you can use other styles of declaration
Array.Sort(arr, new EnglishMarksBasedComparer());
Array.Sort(arr, new MathsMarksBasedComparer());
Array.Sort(arr);
Basically he says that the second parameter is kind of a description for the first parameter. Is he correct or maybe that's just wrong and the source for my confusion?
It's not wrong it's just worded a bit confusingly.
The IComparer is a nullable type (defined by the questionmark at the end of IComparer). This states that the IComparer is optional/does not have to be passed. However as Mahesh Bongani already meantioned in his reply - internaly if you do not provide a comparer it takes the defualt comparer of the object.
So for this particular funtion if you would pass a Array with objects that do not implement a comparable the function wouldn't be able to sort the elements properly.
I have seen IComparer a few times and am unsure what implements it as standard - as far as lists, arrays and things go. I do know that numbers implement it and I think string does too.
You can though custom implement this inferface. If memory serves me correctly, it provides just one method (interface so you have to write logic yourself) that returns an int. -1 (<0) is lower ranked, +1(>0) is higher ranked, 0 is the same.
I would like to sort my list and then use a binary search to find a name in the list and display it.
public abstract class animal {
protected int age
protected string name
public print() {
console.writeline({
age
} + {
name
});
}
animal() {}~animal() {}
}
public class pets {
private List<animal> list = new List<animal>();
public void search(string m) {
int index = list.BinarySearch(m);
if (index == 0)
list[index].print();
}
}
The built-in BinarySearch method does not let you pass in a value of a type other than the type of the list. To use the built-in BinarySearch you will need to either define a class that implements IComparer<Animal> that compares by Name, or make Animal implement IComparable<Animal> so that two animals are compared by name by default. Then you Sort() the list and call BinarySearch, passing an Animal instance that has the name you want to search for.
You'd have to implement your own BinarySearch method to search for an object with a given property value (assuming, of course, that the list is sorted by that same property).
If you just want to use regular linear search methods to search for an animal with a given name, you can use a straight foreach loop, breaking when the item with that name is found, or use the First() Linq method (which basically does the same thing).
Consider this class
public class A
{
float Order
string Name
public A(float order, string name)
{
Order = order;
Name = name;
}
}
If I were to add this to a SortedSet<A> how would it know which member to order it by? How would I specify this, if I even can in the first place. I imagine that the best performance of the set would be sorting it on add, not adding then sorting through Sort().
SortedSet<A> will expect your class to implement IComparable<A>. Alternatively, you can supply a comparator of type IComparer<A> to do the ordering externally to your class, like this:
class ComparerForClassAByOrder : IComparer<A> {
public int Compare(A left, A right) {
return Math.Sign(left.Order-right.Order);
}
}
class ComparerForClassAByName : IComparer<A> {
public int Compare(A left, A right) {
return left.Name.CompareTo(right.Name);
}
}
Now you can create two sorted sets using different comparators:
var soredByOrder = new SortedSet<A>(new ComparerForAByOrder());
var soredByName = new SortedSet<A>(new ComparerForAByName());
Assuming that you change the accessibility so that you can read the members of A from outside the class, you can simply do:
new SortedSet<A>(Comparer<A>.Create((a1, a2) => a1.Order.CompareTo(a2.Order)))
This requires .NET 4.5 or later.
Remember that NaN will compare less than everything else.
If you do not pass in your own comparer to the constructor, it will be checked if each instance of A is of a run-time type which implements IComparable<> or IComparable. If not, an error is produced when adding to the SortedSet<A>.
I'm adding values to a c# generic list while trying to prevent duplicates, but without success. Anyone know of a reason why this code below wouldn't work?
I have a simple class here:
public class DrivePairs
{
public int Start { get; set; }
public int End { get; set; }
}
And here is my method which tries to return a generic list of the above class:
ArrayList found = DriveRepository.GetDriveArray(9, 138);
List<DrivePairs> drivePairs = new List<DrivePairs>();
foreach (List<int> item in found)
{
int count = item.Count;
if (count > 1)
{
for (int i = 0; i < (count - 1); i++)
{
DrivePairs drivePair = new DrivePairs();
drivePair.Start = item[i];
drivePair.End = item[i + 1];
if (!drivePairs.Contains(drivePair))
drivePairs.Add(drivePair);
}
}
}
drivePairs = drivePairs.Distinct().ToList();
As you can maybe see, I have an ArrayList, and each row contains a List<int>. What I'm doing is going through each and adding to a list which contains only pairs. E.g. if my List<int> contains [1,3,6,9] I want to add three entries to my pairs list:
[1,3]
[3,6]
[6,9]
It all works fine apart from not recognising duplicates. I thought this line would be enough:
if (!drivePairs.Contains(drivePair))
drivePairs.Add(drivePair);
but it continues to add them all. Even when I add a Distinct() at the end, it still doesn't remove them. I've also tried adding them to a HashSet, but it still includes all the duplicates.
Anyone know of a reason why the duplicates might not be getting picked up?
Your DrivePairs class does not specify equality, as a result, the Contains method will be using reference equality. Add an Equals method that uses both Start and End to determine equality and you will probably find your code works.
See: Equality Comparisons (C# Programming Guide)
List.Contains Method
This method determines equality by using the default equality
comparer, as defined by the object's implementation of the
IEquatable.Equals method for T (the type of values in the list).
Change your DrivePairs class
public class DrivePairs: IEquatable<DrivePairs>
{
public int Start { get; set; }
public int End { get; set; }
public bool Equals(DrivePairs other)
{
return (this.Start == other.Start && this.End == other.End)
}
}
See: http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx
Hope this helps
You are creating new List<int> objects - these are different objects and when compared to each other, even if they contain identical values (in the same or in different orders), will be evaluated as different as the default comparison method on reference types is a reference comparison.
You need to write a custom comparer that will identify equal lists in the manner your application requires.
I've marked Colin's as the answer, but here was the code just in case it's any use to anyone:
Equality comparer:
public class EqualityComparer : IEqualityComparer<DrivePairs>
{
public bool Equals(DrivePairs x, DrivePairs y)
{
return x.StartHub.Equals(y.Start);
}
public int GetHashCode(DrivePairs obj)
{
return obj.Start.GetHashCode();
}
}
and in the controller:
IEqualityComparer<DrivePairs> customComparer = new EqualityComparer();
IEnumerable<DrivePairs> distinctDrivePairs = drivePairs.Distinct(customComparer);
drivePairs = distinctDrivePairs.ToList();
Thanks for all the help and comments
I have not tested it but I think the default equality test is if it is the same instance. Try overriding the Equals method and make it use your properties.
The DrivePairs class type is a reference type(remember reference type and value type concept). So when you check if DrivePairs varible is already added in List collections or not it return false as every DrivePairs varibale has different memory location from other.
Try using either Dictionary or StringDictionary or any other Key value pair collection. It will definately work.