-
[RESOLVED] Can someone help me with a List comparison?
I have a class called "ImageThumbnail", one field of which is an IO.FileInfo object. I have one ListA =List(Of ImageThumbnail) and another List B =List(Of IO.FileInfo).
I want to find all the ImageThumbnails in ListA whose FileInfo isn't in ListB.
I can't work out the right syntax to do this, despite all the examples in MSDN. I don't think I can take advantage of List.Contains because the FileInfo in ListA won't be the same object as the one I'm interested in in ListB; it will just have certain field values the same. So I have written a Comparer for FileInfos which overrides the Equals method, and that means I can use FileInfo.Equals. But I can't figure out how to fit it all together.
Any suggestions gratefully received. Cheers, BB
-
Re: Can someone help me with a List comparison?
Can you use LINQ?
If so, perhaps something like this
vb.net Code:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim ListA As New List(Of ImageThumbnail)
Dim ListB As New List(Of FileInfo)
Dim result = From it As ImageThumbnail In ListA _
Where Not ContainsFileInfo(ListB, it.FileInfo) _
Select it
End Sub
Private Function ContainsFileInfo(ByVal list As List(Of FileInfo), ByVal fi As FileInfo) As Boolean
For Each f As FileInfo In list
If f.Equals(fi) Then Return True
Next
Return False
End Function
The ContainsFileInfo method is basically a replacement of the usual Contains method, only this time you can implement your own 'equality check' if the usual (reference-equality) is not right.
-
Re: Can someone help me with a List comparison?
which version of vb.net are you using?
can you post the ImageThumbnail class?
-
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
NickThissen
Can you use LINQ?
i was going to suggest linq too
-
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
.paul.
i was going to suggest linq too
Everyone <3 LINQ ;)
-
Re: Can someone help me with a List comparison?
Thanks for your replies. I know next to nothing about Linq (I don't even get weird's joke:confused:) but I'll take it on trust that Nick's method will do the triq.
I was half expecting something out of the quarter of the generic List and its dozens of new extension methods: System.Actions, System.Predicates, IComparables and other stuff I don't understand. I could swear I've seen syntax with a double-barrelled Of T, e.g.:
Code:
Delegate Teabreak As Long (of T) (of More T), Two Lumps Please
(ok, I just made that up, but I hope someone knows what I mean). So far I have tried a few things with lambda functions which are nicely concise. But I found nothing that helped with the problem I raised in this thread. MSDN offers a dozen or so examples under List(Of T).FindAll Method, including this:
Quote:
FindAll(Predicate(Of T)):
Find all books that whose Genre property is "Computer" using the FindComputer predicate delegate.
That looks like it might answer my question. Frustratingly, it is the only one which has a dead link.
So I'll leave this thread unresolved for the moment, to see if anyone can come up with alternatives in that direction.
cheers, BB
-
Re: Can someone help me with a List comparison?
BB post the ImageThumbnail class i'll have a go at a more precise linq assuming you're using vb2008+
-
1 Attachment(s)
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
.paul.
BB post the ImageThumbnail class i'll have a go at a more precise linq assuming you're using vb2008+
That's kind of you, Paul. Basically the ImageThumbnail object is just a package of a small image with a fileInfo. At the moment it also contains several hundred lines of half-baked methods which probably belong somewhere else; but I trust you'll know what to ignore, so here it is as a zip.
cheers, BB
-
Re: Can someone help me with a List comparison?
Did my method not work? Why not?
-
Re: Can someone help me with a List comparison?
try this:
vb Code:
Dim list1 As New List(Of IB_ThumbNail)
Dim list2 As New List(Of IO.FileInfo)
Dim Thumbnails = (From item In list1 _
Where Array.Find(list2.ToArray, Function(i) i Is item.Info) Is Nothing _
Select item).ToArray
Dim list3 As New List(Of IB_ThumbNail)(Thumbnails)
-
Re: Can someone help me with a List comparison?
Hi Nick and Paul, I'm going to need a little time to try your examples out. It looks at first sight that they have a lot in common and I trust both are going to work. I'll do a timing comparison to see if there is any efficiency difference. Anyway, it's clear that I need to study the Linq syntax a bit so that I can apply the technique to different situations.
I'm still interested to hear if there are alternative ways to solve the problem. Let's see what turns up.
cheers, BB
-
Re: Can someone help me with a List comparison?
My example is linq using a lambda function to search the array. Nick's example is nearly identical except it uses a seperate function with a loop, instead of an inline function to search the array.
-
Re: Can someone help me with a List comparison?
Since this is an object that you have created I would recommend adding the relevant functionality to the object. There is nothing wrong with using other methods but I would keep the object functions in the object for OOP perspective.
If you want another approach than you can add ICamparable to the TambNale class and implement its CampareTo method. Here is an example:
vb Code:
Public Class IB_ThumbNail
Inherits UserControl
Implements IDisposable
Implements IComparable(Of IO.FileInfo)
'Some functions here
Public Function CompareTo(ByVal other As System.IO.FileInfo) As Integer Implements System.IComparable(Of System.IO.FileInfo).CompareTo
Return Me._info.FullName.CompareTo(other.FullName)
End Function
End Class
I am comparing the full name of the FileInfo object but you can campare other value(s) at the same time.
-
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
VBDT
I am comparing the full name of the FileInfo object but you can campare other value(s) at the same time.
you can also compare the complete fileinfo object, but i don't see how your example is usable in this scenario.
-
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
VBDT
Since this is an object that you have created I would recommend adding the relevant functionality to the object. There is nothing wrong with using other methods but I would keep the object functions in the object for OOP perspective.
Thanks for your advice, which is certainly not wasted on me. I can sum up my present problem like this: how can I compare two generic lists, when one is Of Type A, and the other is of objects that have a field or property of Type A?
bye, BB
-
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
.paul.
you can also compare the complete fileinfo object, but i don't see how your example is usable in this scenario.
Well two objects are equal if they point to the same object. I don't think this is what he wants to do since a FileInfo object let say A that contains file info of "text.txt" and FileInfo object B that contains file info of "text.txt" are different objects but after all they contain the info of the same file. Should this be considered equal? I think yes.
But I do agree with you that my example isn't helping to solve his problem after reading one more time his requirements. :o
-
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
boops boops
Thanks for your advice, which is certainly not wasted on me. I can sum up my present problem like this: how can I compare two generic lists, when one is Of Type A, and the other is of objects that have a field or property of Type A?
bye, BB
The example I posted will not help you for this, instead i came up with another one that will make the use of "Contains" method of the list. But this way of doing it requires that list type and the parameter type of "Contains" method be the same. In this example I implemented "IEqualityComparer" interface. Here is the example:
vb Code:
Public Class IB_ThumbNail
Implements System.Collections.Generic.IEqualityComparer(Of IB_ThumbNail)
Dim _info As IO.FileInfo
Public ReadOnly Property Info() As IO.FileInfo
Get
Return Me._info
End Get
End Property
Sub New()
End Sub
Sub New(ByVal fileName As String)
Me._info = New IO.FileInfo(fileName)
End Sub
Public Function Equals1(ByVal x As IB_ThumbNail, ByVal y As IB_ThumbNail) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of IB_ThumbNail).Equals
Return x._info.FullName = y._info.FullName
End Function
Public Function GetHashCode1(ByVal obj As IB_ThumbNail) As Integer Implements System.Collections.Generic.IEqualityComparer(Of IB_ThumbNail).GetHashCode
Return obj._info.GetHashCode
End Function
End Class
And this is how you use it:
vb Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim tn As New IB_ThumbNail("test.txt")
Dim b As Boolean = thumbNailList.Contains(tn, CType(New IB_ThumbNail, Global.System.Collections.Generic.IEqualityComparer(Of IB_ThumbNail)))
End Sub
Again, this might not be what you want since you what to compare two lists with different types.
-
Re: Can someone help me with a List comparison?
Thanks again VBDT. Probably it's clear I'm trying to make an image browser. At present each IB_Thumbnail contains a FileInfo and a thumbnail image. When I visit a folder the program generates the thumbnails for all the images it finds there. But that can take time, so I want to cache the IB_Thumbnails and just check the folder for changes. That's why I want to compare FileInfos in the cached thumbnails with those in the folder.
I can't use generic List.Contains because the cached FileInfos will never be the same objects as the FileInfos read from disk -- even when they refer to the same images. Nick's custom Contains function (post #2) presents a feasible alternative. Clearly it depends on having an implementation of FileInfo.Equals: an IEqualityComparer such as the one you posted. That and Paul's solution, both of which use Linq, will probably work very well.
The reason I am hanging out for something else is that I am trying to learn more about the possibilities of generics, and I don't want to take Linq on board for the moment. Surely something must be lurking among all the native methods and extensions of the Generic List? Taking a fresh trawl through msdn today, for example, I noticed that List(Of T).ConvertAll(Of TOutput) could be a way to turn the list of thumbnails into a list of FileInfos. That may turn out to be the way to go.
Instead I could try to work something out using nested For loops. But I get the impression that Microsoft's newer methods will be more efficient as well as neater.
BB
-
Re: Can someone help me with a List comparison?
If you really don't want to use LINQ you can always just loop through all list elements yourself. But really, why? If you can compile to .NET 3.5, then LINQ is so much easier to write and read.
Everyone with the smallest of programming backgrounds will know exactly what is going on if he sees this
Code:
From it As ImageThumbnail In ListA _
Where Not ListB.Contains(it.FileInfo) _
Select it
It's translated to english very easily:
Code:
Select those ImageThumbnails in ListA where ListB does not contain their FileInfo properties
The only 'glitch' is that you cannot use the ListB.Contains method because it does not check the filenames of the FileInfo but merely does a reference check. So you need a separate function (could also probably be done using inline functions or lambda expressions, but I don't know much about that) which makes it look a little less fancy.
Really, you could create your own collection instead of ListB, and implement the contains method in that class the way you want it, and then you can just use ListB.Contains.
My example should work just fine for you, provided that you implemented some kind of Equals function (which you said you did?). If not, you can simply replace the equals check by a check 'If f.FullName = fi.FullName'. Then it would match those FileInfo objects that point to the same file (but are not necessarily the same object).
Perhaps you are thrown off by the fact that I use a 'primitive' loop in the Contains method. But really, all other Contains methods do the exact same loop behind the scenes. Just get .NET Reflector and take a look for yourself. Also, LINQ most probably does very similar loops behind the scenes. It's not some magic new feature that does things completely differently, it is merely 'syntactic sugar' (as I've seen it called) and in the end it all translates to loops.
-
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
NickThissen
If you really don't want to use LINQ you can always just loop through all list elements yourself. But really, why? If you can compile to .NET 3.5, then LINQ is so much easier to write and read.
I have absolutely nothing against LINQ. It's just that I had decided to spend some more time learning about the generics. If LINQ is capable of replacing all the FindAlls, System.Predicates etc. then I agree that it would better to spend my time on that instead. Certainly it's easier to read and I suppose it is more widely applicable.
Meanwhile, after spending far too long struggling with the List alternatives I have come up with something so simple that I am kicking myself for not having thought of it before. Probably you are mentally kicking me too.
I added this little function to the IB_Thumbnail class:
vb.net Code:
Public Function InfoEquals(ByVal Info As IO.FileInfo) As Boolean
With Me.Info
Return .FullName = Info.FullName AndAlso _
.LastWriteTime = Info.LastWriteTime AndAlso _
.Length = Info.Length
End With
End Function
And that makes it easy to do things like this:
vb.net Code:
For Each info As FileInfo In InfoList
Dim f As FileInfo = info
ThumbnailList.Remove(ThumbnailList.Find _
(Function(thumb As IB_ThumbNail) _
thumb.InfoEquals(f)))
Next
I already had an implementation of ICompareEquals for FileInfos, but it seems for the while I can make do without it. (In fact I wonder why people bother with that kind of complication; but that will probably become clear to me sooner or later.)
Quote:
Everyone with the smallest of programming backgrounds will know exactly what is going on if he sees this
Er, are you referring to me?
Code:
Public Class Boops_Boops
Implements IGnorant
'...
End Class
thanks again one and all,
BB
-
Re: Can someone help me with a List comparison?
wouldn't this work?
vb Code:
Public Function InfoEquals(ByVal Info As IO.FileInfo) As Boolean
Return Me.info.Equals(Info)
End Function
-
Re: Can someone help me with a List comparison?
Not without an implementation of ICompareEquals which shadows the default Equals function and compares only the relevant fields. FileInfo is a reference type, so the default Equals would do a bitwise comparison of the whole object.
By way of illustration, FileInfo has a LastAccessed property which changes every time the file is opened. But I don't want to check that when deciding if the image has changed. If I used the default Equals, I would have to regenerate the thumbnail every time the image was viewed.
Besides, ICompareEquals can only compare two objects of the same type. The nice thing about my IB_Thumbnail.InfoEquals function is that it compares a Thumbnail to a FileInfo; and that's what makes the inline function possible.
BB
-
Re: Can someone help me with a List comparison?
point taken then (after i tested it anyway). my linq wouldn't have worked after all.
just a pointer, .findall is used the same way as .find with the difference being it returns an array instead of a single element
-
Re: Can someone help me with a List comparison?
Just a little note here; I am pretty sure that most solutions to your problem mentioned here would result in very similar (if not exactly the same) code (eventually). Whether you use LINQs query syntax or its function syntax, or even the Find method you ended up using: they all use some kind of loop behind the scenes. There is simply no other way to implement a linear search then to check every item (at least until a match is found). Sure, you can do better than linear searches if your data is structured in some special way (dictionaries, heaps, trees, etc), but in a List(Of T) that is not the case. To find an item that matches some specification, you will need to try every item until one is found.
So, I guess in the end you need to use whichever feels more comfortable for you. For me, that is LINQ because it is so easy to read. You might prefer its function syntax, in which case you should use that
In case you're wondering, this is the difference
vb.net Code:
Dim lst As New List(Of Button)
Dim lst2 As New List(Of TreeView)
'query syntax
Dim matches = From b As Button In lst _
Where b.Tag IsNot Nothing _
AndAlso TypeOf b.Tag Is TreeView _
AndAlso DirectCast(b.Tag, TreeView).Nodes.Count = 3 _
Select b
'function syntax
Dim matches2 = lst.Where(Function(b As Button) b.Tag IsNot Nothing _
AndAlso TypeOf b.Tag Is TreeView _
AndAlso DirectCast(b.Tag, TreeView).Nodes.Count = 3)
(Disclaimer: perhaps the two are not exactly the same, because I never use function syntax it might contain some error)
What I'm trying to say is: in the end, it won't matter one bit which method you use.
-
Re: Can someone help me with a List comparison?
Thanks for the Linq syntax examples Nick. I think the comparison will clarify a few things that have been puzzling me.
Quote:
Originally Posted by
NickThissen
What I'm trying to say is: in the end, it won't matter one bit which method you use.
I believe you. In fact I started off using nested For loops but kept getting in a mess; the logic was more difficult than I expected. That's what made me decide to take a fresh look at the List extension methods, inline functions etc. And now that looks like a viable approach.
I don't think performance in processing the lists will be all that crucial in this application anyway. The big time consumer is getting and generating the thumbnails. At the moment, I do that every time I visit a folder, but obviously I can speed it up drastically by caching the thumbnails.
bye, BB
-
Re: Can someone help me with a List comparison?
Quote:
Originally Posted by
boops boops
Thanks again VBDT. Probably it's clear I'm trying to make an image browser. At present each IB_Thumbnail contains a FileInfo and a thumbnail image. When I visit a folder the program generates the thumbnails for all the images it finds there. But that can take time, so I want to cache the IB_Thumbnails and just check the folder for changes. That's why I want to compare FileInfos in the cached thumbnails with those in the folder.
I can't use generic List.Contains because the cached FileInfos will never be the same objects as the FileInfos read from disk -- even when they refer to the same images. Nick's custom Contains function (post #2) presents a feasible alternative. Clearly it depends on having an implementation of FileInfo.Equals: an IEqualityComparer such as the one you posted. That and Paul's solution, both of which use Linq, will probably work very well.
The reason I am hanging out for something else is that I am trying to learn more about the possibilities of generics, and I don't want to take Linq on board for the moment. Surely something must be lurking among all the native methods and extensions of the Generic List? Taking a fresh trawl through msdn today, for example, I noticed that List(Of T).ConvertAll(Of TOutput) could be a way to turn the list of thumbnails into a list of FileInfos. That may turn out to be the way to go.
Instead I could try to work something out using nested For loops. But I get the impression that Microsoft's newer methods will be more efficient as well as neater.
BB
Understood, it is much clear now what you want to do. Sometime ago I have done something like that creating windows browser that was listing all the files with their associated icons. So this is something similar what you are doing. Yes I remember doing the same way as you are trying to do, keep the created objects that have been already created and show them when I visited to the same folder. The way you are doing is different what I have done and requires these steps:
1. No matter what, you will loop through all images in the folder since there might be added some new ones.
2. Some images could be deleted after the last catch so you need to delete the corresponding thumbnail object.
3. Some images could be changed after the last catch so the image that the object contains needs to be updated.
The way I was doing was this: I had a DirectoryWatcher component (it is in the VS toolbar) that was watching all the folders that I have visited and have the cached data. So when a file was added, deleted or changed the corresponding object was changed accordingly. For example: if file was added than I created the object, if the file was deleted I deleted the object and if the file was changed I updated the object. As you can see this way you are not going through and checking all the objects. Also you won’t have the headache of adding and deleting thumbnails. In this way you are dealing with one thumbnail at the time, not looping throw all the files each time you visit the folder.
I thought this approach can be an alternative to what you have now :)
-
Re: Can someone help me with a List comparison?
Yes, it would be interesting to try a DirectoryWatcher. It shouldn't be too difficult to let it trigger a directory refresh in the same way as the user clicking on a TreeView node. And it ought to be possible to suppress the triggering when it might interfere with other processes. I am already using a BackgroundWorker to generate thumbnails from large images, which might prove useful in this connection too.
Regards, BB