Select String that is not a property of another object - c#

I am writing code that will select string keys from an array ApiIds that are not property ApiId of results objects.
I wrote the following code, but it looks redundant to me, is there a way to combine this into one statement and not convert a HashSet of objects into another HashSet of Strings?
var resultsCached = new HashSet<string>(results.Select(x => x.ApiId));
var missingResults = apiIds.Select(x => !resultsCached.Contains(x));
Thanks.

Except will give you the items that aren't in the other collection:
var missingResults = apiIds.Except(results.Select(x => x.ApiId));

Another efficient O(n) approach is to use HashSet.ExceptWith which removes all elements from the set which are in the second sequence:
HashSet<string> apiIdSet = new HashSet<string>(apiIds);
apiIdSet.ExceptWith(results.Select(x => x.ApiId));
The set contains only strings which are not in results now.

Related

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();

Find any entities that contain any one string from a list

I can't quite get my head around this one for some reason.
Say we have a class Foo
public class Foo
{
public string Name {get;set;}
}
And we have a generic list of them. I want to search through the generic list and pick out those that have a Name that contains any from a list of strings.
So something like
var source = GetListOfFoos();//assume a collection of Foo objects
var keywords = GetListOfKeyWords();//assume list/array of strings
var temp = new List<Foo>();
foreach(var keyword in keywords)
{
temp.AddRange(source.Where(x => x.Name.Contains(keyword));
}
This issue here being a) the loop (doesn't feel optimal to me) and b) each object might appear more than once (if the name was 'Rob StackOverflow' and there was a keyword 'Rob' and keyword 'Stackoverflow').
I guess I could call Distinct() but again, it just doesn't feel optimal.
I think I'm approaching this incorrectly - what am I doing wrong?
I want to search through the generic list and pick out those that have
a Name that contains any from a list of strings.
Sounds rather easy:
var query = source.Where(e => keywords.Any(k => e.Name.Contains(k)));
Add ToList() to get results as a List<Foo>:
var temp = query.ToList();
Put the keywords into a HashSet for fast lookup, so that you're not doing a N2 loop.
HashSet<string> keywords = new HashSet<string>(GetListOfKeyWords(), StringComparer.InvariantCultureIgnoreCase);
var query = source.Where(x => keywords.Contains(x.Name));
EDIT: Actually, I re-read the question, and was wrong. This will only match the entire keyword, not see if the Name contains the keyword. Working on a better fix.
I like MarcinJuraszek's answer, but I would also assume you want case-insensitive matching of the keywords, so I'd try something like this:
var query = source.Where(f => keywords.Any(k => f.Name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0));

C# Array, How to make data in an array distinct from each other?

C# Array, How to make data in an array distinct from each other?
For example
string[] a = {"a","b","a","c","b","b","c","a"};
how to get
string[]b = {"a","b","c"}
Easiest way is the LINQ Distinct() command :
var b = a.Distinct().ToArray();
You might want to consider using a Set instead of an array. Sets can't contain duplicates so adding the second "a" would have no effect. That way your collection of characters will always contain no duplicates and you won't have to do any post processing on it.
var list = new HashSet<string> { };
list.Add("a");
list.Add("a");
var countItems = list.Count(); //in this case countItems=1
An array, which you start with, is IEnumerable<T>. IEnumerable<T> has a Distinct() method which can be used to manipulate the list into its distinct values
var distinctList = list.Distinct();
Finally,IEnumerable<T> has a ToArray() method:
var b = distinctList.ToArray();
I think using c# Dictionary is the better way and I can sort by value using LINQ

Is there a way to extract primitive fields from a Dictionary of objects in C#?

Here's what I'm trying to do:
ObjectA
{
int ID;
string name;
}
I want to convert Dictionary to List where the strings in the list are the .name values of the ObjectAs in the dictionary. Obviously I could manually iterate over the dictionary values and build the list that way, but I was hoping there'd be a simpler or faster way in C# / .NET. A LINQ solution is fine, if it's simpler and faster than/as fast as:
List<string> aNames = new List<string>();
foreach(ObjectA a in DictionaryA.Values)
aNames.Add(a.name);
Here's the non-query-expression form of Matthew's answer:
var names = DictionaryA.Values.Select(x => x.name).ToList();
(I tend not to use query expressions when I'm just doing a single select or a single where, especially if I also need to call another method such as ToList.)
Alternatively:
var names = DictionaryA.Select(x => x.Value.name).ToList();
(from val in DictionaryA.Values select val.name).ToList()
There's plenty of ways to do this once you have a query:
IQueryable<string> query = DictionaryA.Values.Select(v => v.name);
//use the Constructor of List<T> that accepts IEnumerable<T>
List<string> aNames = new List<string>(query);
//
//or use the AddRange method for existing Lists
List<string> aNames = new List<string<();
aNames.AddRange(query);
//
//or use the Enumerable.ToList extension method
List<string> aNames = query.ToList();

.NET / C# - Convert List to a SortedList

What is the best way to convert a List to SortedList? Any good way to do it without cycling through it? Any clever way to do it with an OrderBy()?
WRAP UP
Please read all answers and comments.
Do you mean:
you have a List<T> and wish it to be sorted in place?
you have a List<T> and wish to create another 'list' which is itself sorted
you have a List<T> and wish to make a SortedList<T,T> where the key is the same as the value
Assuming input:
var x = new List<int>() { 3, 2, 1 };
1 is trivial
x.Sort();
2 is trivial
// sx is an IOrderedEnumerable<T>, you can call ToList() on it if you want
var sx = x.OrderBy(i => i);
3 is trivial with a copy
var s = new SortedList<int,int>(t.ToDictionary(i => i));
and more efficiently:
var s = new SortedList<int,int>();
foreach (var i in x) { s[i] = [i]; }
I can't see why you would want to do 3 but there you go.
var list = new List<string>();
var sortedList = new SortedList<string, string>(list.ToDictionary(s => s));
Now I have no clue how efficient this is, but it's one line of code :) Also, in this example I just used the string itself as the selector. In a real scenario, you should know ahead of time what you'd like to use as a selector.
Understand that a List<T> is a smart array, and a SortedList<T, U> is a key/value binary tree. Since there's no relationship between their structures, there can't possibly be a more effective way to do it rather than simply taking each element from the list and putting it into the tree.
If you mean "sorted list" instead of "SortedList," then it's trivial to sort your list via either List.Sort() or an appropriate OrderBy().
List unsortedPersons = new List();
// ... Populate unsortedPersons ...
var sorted = from person in unsortedPersons
orderby person.Name
select person;
The LINQ gives you an ISortedEnumerable i believe, which may be good enough for your purposes.

Categories