C# FindAll Method with array of values - c#

I have some kind of ULR object list like below
List<myUrl> urls = new List<myUrl>();
myUrl is a class that contains 4 field and all of them string.
in this list I want to find items does not contain another given list of values
List<myUrl> result = urls.FindAll(
different_from_list_of_values condition;
);
how can I do that?

the best way should be topic, the idea is to override Equals method with your own logic, it have example for two fields class TwoDPoint.
Then you just need to check this topic
instead of p2.ID == p.ID => you would rather have p2.Equals(p)

Related

how to publish a combine string of list values to a message box

I have a while loop that publishes certain values into a string, and i a button which should join the values into a string and show that as a message box.
string toDisplay = string.Join("," , Export.ToString());
MessageBox.Show(toDisplay);
However all i get is the internal list not the values.
export
the list initiator
List<int> Export = new List<int>();
If Export is a List of int then you don't add ToString() in the Join, just leave the list alone
string toDisplay = string.Join("," , Export);
MessageBox.Show(toDisplay);
If you add the ToString() method to Export variable you are asking the list to give a representation of itself as a string, but the generic List<T> doesn't have any override for that and use the underlying ToString() from the base object class. This method just outputs the class name.
Instead if you leave just the List variable name Export then the Join method used is the one that enumerate the list elements (integers) and ask them one by one to give a ToString() representation of themselves. Integers can do that.
It's good to know also some Linq, Aggregate can help:
var toDisplay = (export?.Any() ?? false) ? // not null and not empty
export.Select(m => m.ToString()).Aggregate((m1, m2) => $"{m1},{m2}") :
string.Empty;
Note: But #Steve answer is more efficient from memory allocation point of view

Converting an array and performing a contains

I am reading some data from a database table. One of the fields in the database "VendorList" returns a comma seperated list of Vendors or just one id.
Ex: "1256,553,674" or "346"
There are a couple things I need to do:
Convert this string to an int[]
Perform a "Contains" against an IEnumerable collection.
Return that collection and assign it to a property.
This code is being called inside of a .Select when creating a new object and "Vendor" is a property on that new object.
Here is my code that I am currently using:
Vendors = (m.VendorList.Contains(","))
? (from v in vendors
where m.VendorList.Split(',')
.Select(n => Convert.ToInt32(n))
.ToArray()
.Contains(v.VendorID)
select v).ToList()
: (string.IsNullOrEmpty(m.VendorList))
? null
: (from s in vendors
where s.VendorID == int.Parse(m.VendorList)
select s).ToList()
The code works but it looks very messy and it will be hard to maintain if another developer were to try and refactor this.
I am sort of new to linq, can you provide any tips to clean up this mess?
As you can see I am using two ternary operators. The first one is to detect if its a comma separated list. The second is to detect if the comma separated list even have values.
Try this. I believe it's equivalent to what you're trying to do.. correct me if I'm wrong.
You could do the following in a single line of code, but I think it's more readable (maintainable) this way.
var Vendors = new List<int>();
if (m.VendorList != null)
Vendors.AddRange(vendors.Where(v => m.VendorList
.Split(',')
.Select(y => Convert.ToInt32(y))
.Contains(v))
.Select(v => v));
Vendors = from v in vendors
let vendorList = from idString in m.Split(',')
select int.Parse(idString)
where vendorList.Contains(v.VendorID)
select v;
There is no need to check for the presence of ",".
This is a case where I'd suggest pulling part of this out of your LINQ statement:
var vendorIds = m.VendorList
.Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries)
.Select(n => Convert.ToInt32(n))
.ToArray();
someObj.Vendors = vendors.Where(v => vendorIds.Contains(v.VendorID));
This is more readable. By assigning a variable to vendorIds, you indicate to future programmers what this variable means. They don't have to fully grok all your LINQ code before they can understand the general intent.
This will perform better. In your original code, you are re-parsing the entire vendor list twice for each value in vendors. This code parses it once, and reuses the data structure for all of your ID checks. (If you have large lists of vendor IDs, you can further improve performance by making vendorIds a HashSet<>.)
If your input is an empty string, the RemoveEmptyEntries part will ensure you end up with an empty list of vendor IDs, and hence no matching Vendors. If your input has only one value without commas, you'll end up with a single ID in the list.
Note that this will not behave exactly like your original code, in that it won't set the value to null if given a null or empty m.VendorList. I'm guessing that if you take time to think about it, having a null m.VendorList is not actually something you expect to happen, and it'd be better to "fail fast" if it ever did happen, rather than be left wondering why your .Vendors property ended up null. I'm also guessing that if you have an empty .Vendors property, it will be easier for consuming code to deal with correctly than if they have to check for null values.
You can try this:
string str = "356"; //"1256,553,674";
string[] arr = str.Split(',');
List<int> lst = new List<int>();
foreach (string s in arr)
{
lst.Add(Convert.ToInt32(s));
}
List will contain all numbers in your string
string str = "1256,553,674";
IEnumerable<int> array = str.Split(',').Select(n => Convert.ToInt32(n)).ToArray();

C# return generic list of objects using linq

i got a generic list that looks like this:
List<PicInfo> pi = new List<PicInfo>();
PicInfo is a class that looks like this:
[ProtoContract]
public class PicInfo
{
[ProtoMember(1)]
public string fileName { get; set; }
[ProtoMember(2)]
public string completeFileName { get; set; }
[ProtoMember(3)]
public string filePath { get; set; }
[ProtoMember(4)]
public byte[] hashValue { get; set; }
public PicInfo() { }
}
what i'm trying to do is:
first, filter the list with duplicate file names and return the duplicate objects;
than, filter the returned list with duplicate hash value's;
i can only find examples on how to do this which return anonymous types. but i need it to be a generic list.
if someone can help me out, I'd appreciate it. also please explain your code. it's a learning process for me.
thanks in advance!
[EDIT]
the generic list contains a list of objects. these objects are pictures. every picture has a file name, hash value (and some more data which is irrelevant at this point). some pictures have the same name (duplicate file names). and i want to get a list of the duplicate file names from this generic list 'pi'.
But those pictures also have a hash value. from the file names that are identical, i want another list of those identical files names that also have identical hash values.
[/EDIT]
Something like this should work. Whether it is the best method I am not sure. It is not very efficient because for each element you are iterating through the list again to get the count.
List<PicInfo> pi = new List<PicInfo>();
IEnumerable<PicInfo> filt = pi.Where(x=>pi.Count(z=>z.FileName==x.FileName)>1);
I hope the code isn't too complicated to need explaining. I always think its best to work it out on your own anyway but if anythign is confusing then just ask and I'll explain.
If you want the second filter to be filtering for the same filename and same hash being a duplicate then you just need to extend the lambda in the Count to check against hash too.
Obviously if you just want filenames at the end then it is easy enough to do a Select to get just an enumerable list of those filenames, possibly with a Distinct if you only want them to appear once.
NB. Code written by hand so do forgive typos. May not compile first time, etc. ;-)
Edit to explain code - spoilers! ;-)
In english what we want to do is the following:
for each item in the list we want to select it if and only if there is more than one item in the list with the same filename.
Breaking this down to iterate over the list and select things based on a criteria we use the Where method. The condition of our where method is
there is more than one item in the list with the same filename
for this we clearly need to count the list so we use pi.Count. However we have a condition that we are only counting if the filename matches so we pass in an expression to tell it only to count those things.
The expression will work on each item of the list and return true if we want to count it and false if we don't want to.
The filename we are interested in is on x, the item we are filtering. So we want to count how many items have a filename the same as x.FileName. Thus our expression is z=>z.FileName==x.FileName. So z is our variable in this expression and x.FileName in this context is unchanging as we iterate over z.
We then of course put our criteria in of >1 to get the boolean value we want.
If you wanted those that are duplicates when considering the filename and hashvalue then you would expand the part in the Count to be z=>z.FileName==x.FileName && z.hashValue==x.hashValue.
So your final code to get the distinct on both values would be:
List pi = new List();
List filt = pi.Where(x=>pi.Count(z=>z.FileName==x.FileName && z.hashValue==x.hashValue)>1).ToList();
If you wanted those that are duplicates when considering the filename and hashvalue then you would expand the part in the Count to compare the hashValue as well. Since this is an array you will want to use the SequenceEqual method to compare them value by value.
So your final code to get the distinct on both values would be:
List<PicInfo> pi = new List<PicInfo>();
List<PicInfo> filt = pi.Where(x=>pi.Count(z=>z.FileName==x.FileName && z.hashValue.SequenceEqual(x.hashValue))>1).ToList();
Note that I didn't create the intermediary list and just went straight from the original list. You could go from the intermediate list but the code would be much the same if going from the original as from a filtered list.
I think, you have to use SequenceEqual method for finding dublicate
(http://msdn.microsoft.com/ru-ru/library/bb348567.aspx).
For filter use
var p = pi.GroupBy(rs => rs.fileName) // group by name
.Where(rs => rs.Count() > 1) // find group whose count greater than 1
.Select(rs => rs.First()) // select 1st element from each group
.GroupBy(rs => rs.hashValue) // now group by hash value
.Where(rs => rs.Count() > 1) // find group has multiple values
.Select(rs => rs.First()) // select first element from group
.ToList<PicInfo>() // make the list of picInfo of result

List of classes question c#

I have a class contain many variables, something like that
class test
{
internal int x , y ;
internal string z;
}
I created a list of this class list<test> c
I want to do the following:
test if all the list items contain the same x
get the list's item that has z = "try"
I need a quick and fast way , instead of iterate though the entire items
Any suggestion please ,
LINQ to Objects is your friend. For the first:
bool allSameX = list.All(t => t.x == list[0].x);
Test firstTry = list.First(t => t.z == "try");
Test firstTryOrNull = list.FirstOrDefault(t => t.z == "try");
The first one depends on there being at least one value of course. Alternatives might be:
bool allSameX = !list.Select(t => t.x)
.Distinct()
.Skip(1)
.Any();
In other words, once you've gone past the first distinct value of x, there shouldn't be any more. One nice aspect of this is that as soon as it spots the second distinct value, it will stop looking - as does the first line (the All version) of course.
LINQ is wonderfully flexible, and well worth looking into closely.
EDIT: If you need to do the latter test ("find an element with a particular value for z") for multiple different values, you might want a dictionary or a lookup, e.g.
// If there are duplicate z values
var lookup = list.ToLookup(t => t.z);
// If z values are distinct
var dictionary = list.ToDictionary(t => t.z);
Without some pre-work, there's no way of performing the queries you want without iterating over at least some of the list.
You can use linq. Here is a link to small examples that will help you a lot for future too http://msdn.microsoft.com/en-us/vcsharp/aa336746
You could implement a custom collection class instead of a list, and put the search smarts into this e.g.
add a method AllItemsHaveSameX() and a private bool field allItemsHaveSameX
expose a dictionary keyed by the search strings with the index of the item that has that value.
When adding/removing items:
You would re-evaluate allItemsHaveSameX
Add/remove from your private dictionary.

Select a distinct list of words from an array with LINQ

I'm trying to get a distinct list of words from an array of words with the following code:
string words = "this is a this b";
var split = words.Split(' ');
IEnumerable<Word> distinctWords = (
from w in split
select new Word
{
Text = w.ToString()
}
).Distinct().ToList();
I thought this would take out the double occurrence of 'this' but it returns a list of each word in the phrase.
In your example, each Word object is distinct, because there is no comparison which looks at the Text property.
However, there's no reason to create a new object:
var distinctWords = (from w in split
select w).Distinct().ToList();
Or more simply:
var distinctWords = new List<string>(split.Distinct());
The problem is, that you create several Word objects that contain the same Value, but how should the compiler know, that these shall be the same items?
Try
(from w in split.Distinct()
select new Word { Text = w.ToString()}).ToList();
You haven't posted the code for your Word class, but my guess is that it doesn't implement Equals with a value comparison so you get the default implementation of Equals which just checks the object references. Note that if you decide to implement your own version of Equals, you also need to correctly implement GetHashCode.
An alternative way to solve this issue is to provide an IEqualityComparer as a parameter to the Distinct function.
You may try to convert array ToList() first before calling the .Distinct()
and then converting it ToArray() again
myArray= myArray.ToList().Distinct().ToArray();
As others noted, the problem is probably that your Word object doesn't implement structural equality (compare the actual content, not instance references). If you still want to get a collection of Word objects as the result, but use Distinct on the underlying string values, you can write this:
IEnumerable<Word> distinctWords =
(from w in split.Distinct()
select new Word { Text = w.ToString() }).ToList();

Categories