list vclAsset<FullAsset>
list callsigns<string>
foreach(FullAsset fa in vclAsset)
{
if (callsigns.contains(fa.asset.callsign))
{
//do something
}
}
Is there a more elegant way to do the above? A FullAsset object contains an Asset object which in turn has a string "Callsign." Each callsign will be unique, so my list callsigns will only have one of each string, and no two FullAsset objects will share an Asset.callsign variable.
In a nutshell I want to pull all the FullAssets that have a certain callsign, but using a foreach seems clumsy (given that the number of FullAssets that could be contained in said list potentially has no upper limit).
You could use a lambda expression, something like this:
foreach(FullAsset fa in vclAsset.Where(a => callsigns.contains(a.asset.callsign))
{
// do something
}
If your keys are unique, you can use a Dictionary or a Hashtable to speed up searching.
If you only want to find a certain item, you can use the List<T>.Find method and supply a predicate.
FullAsset result = vclAsset.Find
(fa => callsigns.contains(fa.asset.callsign));
or
List<FullAsset> results = vclAsset.FindAll
(fa => callsigns.contains(fa.asset.callsign));
If you are using .Net 3.5, LINQ Where may be a better solution, as others have stated, since it returns an enumerator (lazy evaluation) vs a full List.
Sure, using linq.
var assets= vclAsset.Where(fullA=>allsigns.contains(fullA.asset.callsign));
assets will be some enumerable structure.
I can recommend 100 Linq samples for inspiration and learning
Not sure if it counts as more elegant but you can use linq...
var results = from fa in vclAsset
where callsigns.Contains(fa.asset.callsign)
select fa;
var result = vclAsset.Where(x=>callsigns.Any(y=>x.asset.callsign==y));
P.s. I would rename vclAsset and asset/callsign properties.
You can also use the Join function to do this.
var sortedList = vclAsset.Join(callsigns,
x => x.asset.callsign, x => x,
x, y => x);
This is the list of vclAssets that have the listed callsign.
Related
If I have a list of objects that have the properties fruitName and numFruits and I want to pluralize the fruitName where numFruits is greater than 1, is it possible to do that in a single statement by chaining together Where and Foreach?
Something like:
fruitList.Where(fl => fl.numFruits > 1).ForEach(fl => fl.fruitName = fl.fruitName + "s");
I tried the above and it doesn't work. It complains that System.Collections.Generic.IEnumerable doesn't contain a definition for ForEach.
Typically you want to use foreach the language construct when possible. Eric Lippert has a blog post going into additional detail as to why.
Loops are good when you are doing modifications as it makes finding those modifications easier.
foreach (var fl in fruitList.Where(fl => fl.numFruits > 1))
{
fl.fruitName = fl.fruitName + "s";
}
Is more straightforward and accomplishes the same task.
If you really want a one-liner (it will be harder to maintain) and want to keep the original list intact but only modify some of the elements, you'll have to use a full anonymous function. If you need multiple statements (a block of code), you'll need to include the braces and statement-terminating semicolons like below:
fruitList.ForEach(fl => { fl.fruitName = fl.numFruits > 1 ? fl.fruitName + "s" : fl.fruitName; });
This works on the original list (no subset) and does basically the exact same thing a structured foreach would do.
There's a good blog post by Eric Lippert on why there is no “ForEach” sequence operator extension method, essentially the reason is:
The first reason is that doing so violates the functional programming
principles that all the other sequence operators are based upon.
Clearly the sole purpose of a call to this method is to cause side
effects. The purpose of an expression is to compute a value, not to
cause a side effect. The purpose of a statement is to cause a side
effect. The call site of this thing would look an awful lot like an
expression (though, admittedly, since the method is void-returning,
the expression could only be used in a “statement expression”
context.) It does not sit well with me to make the one and only
sequence operator that is only useful for its side effects.
If you wanted to do this in a single statement you could use a .Select()
var newFruitList = fruitList.Where(fl => fl.numFruits > 1).Select(fl => fl.fruitName + "s");
Like #Tim Schmelter suggested, you can use ToList() to convert to a list and then use the ForEach method on the result returned. Although the ToList() might return a shorter list based on the filter, the original objects themselves would be changed and your fruitList will remain unchanged.
fruitList.Where(fl => fl.numFruits > 1).ToList().ForEach(fl => fl.fruitName = fl.fruitName + "s");
// fruitList still has all elements
You can use the static Array.ForEach method to update the list.
Array.ForEach(fruitList.Where(fl => fl.numFruits > 1).ToArray(), x => { x.fruitName += "s"; });
Given that "append an s" doesn't actually give you the correct answer for many fruits, any approach that does that will give you an incorrect answer, no matter how well it does it.
Consider using a lookup table to map singlular to plurals (and vice versa) instead:
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
private static Dictionary<string, string> fruitLookup =
new Dictionary<string, string>
{
{"blueberry", "blueberries"},
{"peach", "peaches"},
{"apple", "apples"}
};
public static void Main()
{
var fruitList = new List<string> {"blueberry", "peach", "apple"};
// Here is your one-line conversion:
var plurals = fruitList.Select(f => fruitLookup[f]).ToList();
foreach (var p in plurals)
{
Console.WriteLine(p);
}
}
}
I'm trying to add an extra parameter to a list of ef objects to track processing, but I keep running into having to initialize each list item explicitly. What's the correct linq way to do this? Aside from terseness, is there any advantage to a linq syntax in this case?
List<app_subjects> subjectList = AppMySQLQueries.GetAllSubjects();
List<Tuple<app_subjects, bool>> subjectCollection = new List<Tuple<app_subjects, bool>>(subjectList.Count);
foreach (app_subjects subject in subjectList)
{
subjectCollection.Add(Tuple.Create(subject, false));
}
I have searched the site without success.
You just want to use a projection here ( Select ) which applies the transformation in your lambda expression to each element in the source collection.
List<Tuple<app_subjects, bool>> tuples = subjectList.Select(x => new Tuple<app_subjects, bool>(x, false)).ToList();
The ToList() call is not entirely necessary, if you removed it then the method will return an IEnumerable<Tuple<app_subjects, bool>>. If you're just going to iterate the collection of tuples afterwards the ToList call should be removed as it forces execution (enumerates the IEnumberable) and then your next operation (the foreach) would do the same, making the code perform worse.
Like this?
subjectList.Select(s => Tuple.Create(s, false)).ToList();
With C# 10.0 (.NET 6.0) this is even easier and cleaner. Along with named tuples we can also declare a tuple by simply putting the values in round brackets.
List<(string NamedProperty1, int NamedProperty2)> _tuples = new();
_tuples = _objectList.Select(o => (o.SomeProperty1, o.SomeProperty2)).ToList();
try this.
List<Tuple<app_subjects, bool>> subjectCollection = subjectList.CovertAll( subject => new Tuple<app_subjects, bool>(){
subject,
false
}).ToList();
I safely search a list for an object like this:
var someResult = myList.FirstOrDefault(x=>x.SomeValue == "SomethingHere");
If there are no objects that match my criteria then someResult is going to be null.
But if I only have the index of the object I want, things are not so nice. I seem to have to so something like this:
try
{
var someResult = myList[4];
}
catch (ArgumentOutOfRangeException)
{
someResult = null;
}
I admit that is not terrible to have to write. But it seems to me that there should be a way to just have the list return null if the index ends up being bogus.
Is there away to have a one (or two) line look up using existing .net methods?
(I know I could easily write an extension method, but I am wondering if there is a built in way to do this.)
I think you want C.B.s suggestion of ElementAtOrDefault - go vote it up.
I have a few other points to add...
Regarding your try/catch solution:
I admit that is not terrible to have to write.
Please don't use exceptions for this. Your approach is more verbose, exceptions are slow so you will get poor performance, and exceptions should only be used for exceptional situations.
It's also fairly easy to write this without LINQ by using the conditional operator:
var someResult = myList.Count > 4 ? myList[4] : null;
You may be able to use the LINQ extension .ElementAtOrDefault() to achieve what you want.
List<Foo> foos = new List<Foo>();
Foo element = foos.ElementAtOrDefault(4);
However, you need to be careful that your generic type to List<T> is a reference type or a string, so the "default" returned to you is actually null. The default you get back is default(T).
Yes, you can also use LINQ when you only have the index:
var someResult = myList
.Select((x, i) => new { X = x, Index = i })
.FirstOrDefault(x => x.Index == 4);
Enumerable.Select Method (IEnumerable, Func)
Projects each element of a sequence into a new form by incorporating
the element's index.
C B's answer wins, but I can compete for second place, right?
var someResult = myList.Skip(4).FirstOrDefault();
I want to invoke Queryable.Where() and get all elements. There's no version of Where() that works without a predicate function. So I have to right this:
var result = table.Where( x => true );
and it works but that feels really stupid to me - x is never used, and there's no "transformation" for the => "arrow" symbol.
Is there a more elegant solution?
You can use the following, which is more elegant:
var result = table;
You could also omit result completely, and use table directly.
Isn't table.Where(x=>true) essentially a noop? I mean, what is the point? You can do use _ instead of x though, which is idiomatic.
table.Where(_=> true);
But really, the following is what you are doing:
for (var item in table)
{
if (true) // your Where() clause..
{
yield item;
}
}
See how it doesn't really make sense?
table.Where( x => true ) is not "returning all elements". It simply returns an enumerable that has enough information to return some subset of elements when it is being enumerated upon. Until you enumerate it, no elements are "returned".
And since this subset is not even proper in this case (i.e. all elements are returned), this is essentially a no-op.
To enumerate over all elements, write a simple foreach, or use ToList or ToArray or if you don't care about actually returning any elements (and just want to enumerate, presumably for side-effects): table.All(x => true) or table.Any(x => false), or even just table.Count().
In this case you would not need to call Where because you are not filtering the Queryable.
If you still wish to call Where and you do this in many places you could define a static Func and reuse that:
public static Func<int, bool> t = ReturnTrue;
public static bool ReturnTrue(int i)
{
return true;
}
table.Where(t);
If you're trying to get a copy of the contents of table instead of a reference,
var result = table.ToList();
but it's not clear if that's really what you're trying to accomplish. Details?
I have a List. For valid reasons, I duplicate the List many times and use it for different purposes. At some point I need to check if the contents of all these collections are same.
Well, I know how to do this. But being a fan of "short hand" coding(linq...) I would like to know if I can check this EFFICIENTLY with the shortest number of lines of code.
List<string> original, duplicate1, duplicate2, duplicate3, duplicate4
= new List<string();
//...some code.....
bool isequal = duplicate4.sequenceequal(duplicate3)
&& duplicate3.sequenceequal(duplicate2)
&& duplicate2.sequenceequal(duplicate1)
&& duplicate1.sequenceequal(original);//can we do it better than this
UPDATE
Codeinchaos pointed out certain senarios I havent thought of(duplicates and order of list).Though sequenceequal will take care of duplicates the order of the list can be a problem. So I am changing the code as follows. I need to copy the Lists for this.
List<List<string>> copy = new List<List<int>> { duplicate1, duplicate2,
duplicate3, duplicate4 };
bool iseqaul = (original.All(x => (copy.All(y => y.Remove(x))))
&& copy.All(n => n.Count == 0));
UPDATE2
Thanks to Eric-using a HashSet can be very efficient as follows. This wont cover duplicates though.
List<HashSet<string>> copy2 =new List<HashSet<string>>{new HashSet<string>(duplicate1),
new HashSet<string>(duplicate2),
new HashSet<string> duplicate3),
new HashSet<string>(duplicate4)};
HashSet<string> origninalhashset = new HashSet<string>(original);
bool eq = copy2.All(x => origninalhashset.SetEquals(x));
UPDATE3
Thanks to Eric - The original code in this post with SequenceEqual will work with sorting. As Sequenceequal will consider the order of collections, the collections need to be sorted before calling sequenceequal. I guess this is not much of a probelm as sorting is pretty fast(nlogn).
UPDATE4
As per Brian's suggestion, I can use a lookup for this.
var originallkup = original.ToLookup(i => i);
var lookuplist = new List<ILookup<int, int>>
{ duplicate4.ToLookup(i=> i),
duplicate3.ToLookup(i=> i),
duplicate2.ToLookup(i=> i),
duplicate1.ToLookup(i=> i)
};
bool isequal = (lookuplist.Sum(x => x.Count) == (originallkup.Count * 4)) &&
(originallkup.All(x => lookuplist.All(i => i[x.Key].Count() == x.Count())));
Thank you all for your responses.
I have a List. I duplicate the List many times and use it for different purposes. At some point I need to check if the contents of all these collections are same.
A commenter then asks:
Is the order important? Or just the content?
And you respond:
only the content is important
In that case you are using the wrong data structure in the first place. Use a HashSet<T>, not a List<T>, to represent an unordered collection of items that must be cheaply compared for set equality.
Once you have everything in hash sets instead of lists, you can simply use their SetEquals method to see if any pair of sets is unequal.
Alternatively: keep everything in lists, until the point where you want to compare for equality. Initialize a hash set from one of the lists, and then use SetEquals to compare that hash set to every other list.
I honestly can't think of a more efficient solution, but as for reducing the number of lines of code, give this a bash:
var allLists = new List<List<string>>() { original, duplicate1, duplicate2, duplicate3, duplicate4 };
bool allEqual = allLists.All(l => l.SequenceEqual(original));
Or, use the Any operator - might be better in terms of performance.
bool allEqual = !allLists.Any(l => !l.SequenceEqual(original));
EDIT: Confirmed, Any will stop enumerating the source once it determines a value. Thank you MSDN.
EDIT # 2: I have been looking into the performance of SequenceEquals. This guy has a nice post comparing SequenceEquals to a more imperative function. I modified his example to work with List<string> and my findings match his. It would appear that as far as performance is concerned, SequenceEquals isn't high on the list of preferred methods.
You can use reflection to create a generic comparer, and always use it. Look this thread, has a loot of code that can help you: Comparing two collections for equality irrespective of the order of items in them