C# split list into two lists using bool function [duplicate] - c#

This question already has answers here:
Can I split an IEnumerable into two by a boolean criteria without two queries?
(6 answers)
Closed last year.
Say I have a List<string> listOfStrings and I want to divide this list into two lists based on some predicate. E.g., the first list should contain all strings that start with a letter, and second is a list of strings that don't.
Now I would do this like this:
var firstList = listOfStrings.Where(str => predicate(str));
var secondList = listOfStrings.Where(str => !predicate(str));
Is there a better way of doing this in one line?

You can use Linq's GroupBy():
var splitted = listOfStrings.GroupBy(s => Char.IsLetter(s[0]));
And with your predicate, it would be:
Func<string, bool> predicate;
var splitted = listOfStrings.GroupBy(predicate);
Usage:
The easiest way would be to convert the grouped data into a Dictionary<bool, IEnumerable<string>>, when the key is a bool that denotes whether the items in it start with a letter:
var splitted = list.GroupBy(x => Char.IsLetter(x[0]))
.ToDictionary(x => x.Key, z => z.ToArray());
var startWithLetter = splitted[true];
var dontStartWithLetter = splitted[false];
Of course, there are many ways to massage the data into your desired structure, but the above is pretty concise in my opinion.
See MSDN

Kotlin has partition function (sources). C# version:
var (first, second) = list.Partition(x => x.IsTrue);
Extension:
public static (IEnumerable<T> first, IEnumerable<T> second) Partition<T>(this IEnumerable<T> list, Func<T, bool> predicate)
{
var lookup = list.ToLookup(predicate);
return (lookup[true], lookup[false]);
}
Could be more convenient to return List<T>, or to use GroupBy or something else, depending on use case.

You can use 'GroupBy' or 'ToLookup', based on what you will be doing with the results.
Check also lookup vs. groupby

I would do something like this:
class Program
{
static void Main(string[] args)
{
Func<string, bool> startsWithA = s => s[0] == 'a';
List<string> listOfStrings = new List<string>()
{
"abc",
"acb",
"bac",
"bca",
"cab",
"cba"
};
Dictionary<bool, List<string>> dictionaryOfListsOfStrings = listOfStrings.GroupBy(startsWithA).ToDictionary(x => x.Key, x => x.ToList());
}
}

Related

Is there a way to look for a list of ids in a list of parent object type? [duplicate]

I have a list with some identifiers like this:
List<long> docIds = new List<long>() { 6, 1, 4, 7, 2 };
Morover, I have another list of <T> items, which are represented by the ids described above.
List<T> docs = GetDocsFromDb(...)
I need to keep the same order in both collections, so that the items in List<T> must be in the same position than in the first one (due to search engine scoring reasons). And this process cannot be done in the GetDocsFromDb() function.
If necessary, it's possible to change the second list into some other structure (Dictionary<long, T> for example), but I'd prefer not to change it.
Is there any simple and efficient way to do this "ordenation depending on some IDs" with LINQ?
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();
Since you don't specify T,
public static IEnumerable<T> OrderBySequence<T, TId>(
this IEnumerable<T> source,
IEnumerable<TId> order,
Func<T, TId> idSelector)
{
var lookup = source.ToDictionary(idSelector, t => t);
foreach (var id in order)
{
yield return lookup[id];
}
}
Is a generic extension for what you want.
You could use the extension like this perhaps,
var orderDocs = docs.OrderBySequence(docIds, doc => doc.Id);
A safer version might be
public static IEnumerable<T> OrderBySequence<T, TId>(
this IEnumerable<T> source,
IEnumerable<TId> order,
Func<T, TId> idSelector)
{
var lookup = source.ToLookup(idSelector, t => t);
foreach (var id in order)
{
foreach (var t in lookup[id])
{
yield return t;
}
}
}
which will work if source does not zip exactly with order.
Jodrell's answer is best, but actually he reimplemented System.Linq.Enumerable.Join. Join also uses Lookup and keeps ordering of source.
docIds.Join(
docs,
i => i,
d => d.Id,
(i, d) => d);
One simple approach is to zip with the ordering sequence:
List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create)
.OrderBy(x => x.Item2).Select(x => x.Item1).ToList();

Get identical elements in two lists with LINQ [duplicate]

I have two lists:
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
I want do to something like
var newData = data1.intersect(data2, lambda expression);
The lambda expression should return true if data1[index].ToString() == data2[index]
You need to first transform data1, in your case by calling ToString() on each element.
Use this if you want to return strings.
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
var newData = data1.Select(i => i.ToString()).Intersect(data2);
Use this if you want to return integers.
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
var newData = data1.Intersect(data2.Select(s => int.Parse(s));
Note that this will throw an exception if not all strings are numbers. So you could do the following first to check:
int temp;
if(data2.All(s => int.TryParse(s, out temp)))
{
// All data2 strings are int's
}
If you have objects, not structs (or strings), then you'll have to intersect their keys first, and then select objects by those keys:
var ids = list1.Select(x => x.Id).Intersect(list2.Select(x => x.Id));
var result = list1.Where(x => ids.Contains(x.Id));
From performance point of view if two lists contain number of elements that differ significantly, you can try such approach (using conditional operator ?:):
1.First you need to declare a converter:
Converter<string, int> del = delegate(string s) { return Int32.Parse(s); };
2.Then you use a conditional operator:
var r = data1.Count > data2.Count ?
data2.ConvertAll<int>(del).Intersect(data1) :
data1.Select(v => v.ToString()).Intersect(data2).ToList<string>().ConvertAll<int>(del);
You convert elements of shorter list to match the type of longer list. Imagine an execution speed if your first set contains 1000 elements and second only 10 (or opposite as it doesn't matter) ;-)
As you want to have a result as List, in a last line you convert the result (only result) back to int.
public static List<T> ListCompare<T>(List<T> List1 , List<T> List2 , string key )
{
return List1.Select(t => t.GetType().GetProperty(key).GetValue(t))
.Intersect(List2.Select(t => t.GetType().GetProperty(key).GetValue(t))).ToList();
}

Check if list<t> contains any of another list

I have a list of parameters like this:
public class parameter
{
public string name {get; set;}
public string paramtype {get; set;}
public string source {get; set;}
}
IEnumerable<Parameter> parameters;
And a array of strings i want to check it against.
string[] myStrings = new string[] { "one", "two"};
I want to iterate over the parameter list and check if the source property is equal to any of the myStrings array. I can do this with nested foreach's but i would like to learn how to do it in a nicer way as i have been playing around with linq and like the extension methods on enumerable like where etc so nested foreachs just feel wrong. Is there a more elegant preferred linq/lambda/delegete way to do this.
Thanks
You could use a nested Any() for this check which is available on any Enumerable:
bool hasMatch = myStrings.Any(x => parameters.Any(y => y.source == x));
Faster performing on larger collections would be to project parameters to source and then use Intersect which internally uses a HashSet<T> so instead of O(n^2) for the first approach (the equivalent of two nested loops) you can do the check in O(n) :
bool hasMatch = parameters.Select(x => x.source)
.Intersect(myStrings)
.Any();
Also as a side comment you should capitalize your class names and property names to conform with the C# style guidelines.
Here is a sample to find if there are match elements in another list
List<int> nums1 = new List<int> { 2, 4, 6, 8, 10 };
List<int> nums2 = new List<int> { 1, 3, 6, 9, 12};
if (nums1.Any(x => nums2.Any(y => y == x)))
{
Console.WriteLine("There are equal elements");
}
else
{
Console.WriteLine("No Match Found!");
}
If both the list are too big and when we use lamda expression then it will take a long time to fetch . Better to use linq in this case to fetch parameters list:
var items = (from x in parameters
join y in myStrings on x.Source equals y
select x)
.ToList();
list1.Select(l1 => l1.Id).Intersect(list2.Select(l2 => l2.Id)).ToList();
var list1 = await _service1.GetAll();
var list2 = await _service2.GetAll();
// Create a list of Ids from list1
var list1_Ids = list1.Select(l => l.Id).ToList();
// filter list2 according to list1 Ids
var list2 = list2.Where(l => list1_Ids.Contains(l.Id)).ToList();

Linq Order by a specific number first then show all rest in order

If i have a list of numbers:
1,2,3,4,5,6,7,8
and I want to order by a specific number and then show the rest.
For example if i pick '3' the list should be:
3,1,2,4,5,6,7,8
Looking for linq and c#.
Thank you
You can use a comparison in OrderBy or ThenBy to perform a conditional sorting.
list.OrderByDescending(i => i == 3).ThenBy(i => i);
I use OrderByDescending because i want matching results first(true is "higher" than false).
A couple of answers already sort the last few numbers (which may be correct since you're only showing an already sorted list). If you want the "unselected" numbers to be displayed in their original, not necessarily sorted order instead of sorted, you can instead do;
int num = 3;
var result = list.Where(x => x == num).Concat(list.Where(x => x != num));
As #DuaneTheriot points out, IEnumerable's extension method OrderBy does a stable sort and won't change the order of elements that have an equal key. In other words;
var result = list.OrderBy(x => x != 3);
works just as well to sort 3 first and keep the order of all other elements.
Maybe something like this:
List<int> ls=new List<int>{1,2,3,4,5,6,7,8};
int nbr=3;
var result= ls.OrderBy (l =>(l==nbr?int.MinValue:l));
public static IEnumerable<T> TakeAndOrder<T>(this IEnumerable<T> items, Func<T, bool> f)
{
foreach ( var item in items.Where(f))
yield return item;
foreach (var item in items.Where(i=>!f(i)).OrderBy(i=>i))
yield return item;
}
var items = new [] {1, 4, 2, 5, 3};
items.TakeAndOrder(i=> i == 4);
Using #joachim-isaksson idea I came up with this extension method:
public static IOrderedEnumerable<TSource> OrderByWithGivenValueFirst<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
TKey value
)
=> source.OrderBy(x => !keySelector(x).Equals(value));
Test:
[TestFixture]
public class when_ordering_by_with_given_value_first
{
[Test]
public void given_value_is_first_in_the_collection()
{
var languages = new TestRecord[] {new("cs-CZ"), new("en-US"), new("de-DE"), new("sk-SK")};
languages.OrderByWithGivenValueFirst(x => x.Language, "en-US")
.ShouldBe(new TestRecord[] {new("en-US"), new("cs-CZ"), new("de-DE"), new("sk-SK")});
}
private record TestRecord(string Language);
}
You can try with below code with list of dynamic string values
var defaultSortingInternalTrades = ["E,F,G"];
var ItemsToSort = ["A","B","C","D","E",...];
var fData = items.Where(d => defaultSortingInternalTrades.Contains(d.ToString()))
.OrderBy(x => defaultSortingInternalTrades.IndexOf(x.ToString())).ToList();
var oData = items.Where(d => !defaultSortingInternalTrades.Contains(d.ToString())).ToList();
fData.AddRange(oData);

Can I split an IEnumerable into two by a boolean criteria without two queries?

Can I split an IEnumerable<T> into two IEnumerable<T> using LINQ and only a single query/LINQ statement?
I want to avoid iterating through the IEnumerable<T> twice. For example, is it possible to combine the last two statements below so allValues is only traversed once?
IEnumerable<MyObj> allValues = ...
List<MyObj> trues = allValues.Where( val => val.SomeProp ).ToList();
List<MyObj> falses = allValues.Where( val => !val.SomeProp ).ToList();
Some people like Dictionaries, but I prefer Lookups due to the behavior when a key is missing.
IEnumerable<MyObj> allValues = ...
ILookup<bool, MyObj> theLookup = allValues.ToLookup(val => val.SomeProp);
// does not throw when there are not any true elements.
List<MyObj> trues = theLookup[true].ToList();
// does not throw when there are not any false elements.
List<MyObj> falses = theLookup[false].ToList();
Unfortunately, this approach enumerates twice - once to create the lookup, then once to create the lists.
If you don't really need lists, you can get this down to a single iteration:
IEnumerable<MyObj> trues = theLookup[true];
IEnumerable<MyObj> falses = theLookup[false];
You can use this:
var groups = allValues.GroupBy(val => val.SomeProp);
To force immediate evaluation like in your example:
var groups = allValues.GroupBy(val => val.SomeProp)
.ToDictionary(g => g.Key, g => g.ToList());
List<MyObj> trues = groups[true];
List<MyObj> falses = groups[false];
Copy pasta extension method for your convenience.
public static void Fork<T>(
this IEnumerable<T> source,
Func<T, bool> pred,
out IEnumerable<T> matches,
out IEnumerable<T> nonMatches)
{
var groupedByMatching = source.ToLookup(pred);
matches = groupedByMatching[true];
nonMatches = groupedByMatching[false];
}
Or using tuples in C# 7.0
public static (IEnumerable<T> matches, IEnumerable<T> nonMatches) Fork<T>(
this IEnumerable<T> source,
Func<T, bool> pred)
{
var groupedByMatching = source.ToLookup(pred);
return (groupedByMatching[true], groupedByMatching[false]);
}
// Ex.
var numbers = new [] { 1, 2, 3, 4, 5, 6, 7, 8 };
var (numbersLessThanEqualFour, numbersMoreThanFour) = numbers.Fork(x => x <= 4);
Modern C# example using just Linq, no custom extension methods:
(IEnumerable<MyObj> trues, IEnumerable<MyObj> falses)
= ints.Aggregate<MyObj,(IEnumerable<MyObj> trues, IEnumerable<MyObj> falses)>(
(new List<MyObj>(),new List<MyObj>()),
(a, i) => i.SomeProp ? (a.trues.Append(i), a.falses) : (a.trues, a.falses.Append(i))
);
Does this answer the question, yes; is this better or more readable than a foreach, no.
In all of these answers you lose LINQ's 2nd greatest power (after expressiveness of course); laziness! When we call ToDictionary() or ToLookup() we are forcing an enumeration.
Let's take a look at the implementation of partition in Haskell, a great lazy functional programming language.
From Hoogle:
'partition' p xs = ('filter' p xs, 'filter' (not . p) xs)
As you can see it, partition is an expression which returns a tuple of two other expressions. First, where the predicate is applied to the elements and second, where the inverse of the predicate is applied to the elements. Haskell is lazily evaluated implicitly in this case, similar to how LINQ is lazy through its usage of expressions rather than delegates.
So why don't we implement our partition extension method the same way. In LINQ, filter is called where so lets use that.
public static (IEnumerable<T>, IEnumerable<T>) Partition<T>(
this IEnumerable<T> source, Func<T, bool> predicate)
=> (source.Where(predicate), source.Where(x => !predicate(x)));
One caveat with this is that if you force an evaluation on the matches AND the rest, you will perform a double enumeration. However, don't try to optimise early. With this approach you can express partition in terms of LINQ thereby preserving its beneficial characteristics.
Had some fun coming up with this extension method based on the ToLookup suggestion in other answers:
public static (IEnumerable<T> XS, IEnumerable<T> YS) Bifurcate<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
var lookup = source.ToLookup(predicate);
return (lookup[true], lookup[false]);
}
The callsite will look like this:
var numbers = new []{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var (evens, odds) = numbers.Bifurcate(n => n % 2 == 0);
I think the usability of this is nice which is why I'm posting this answer.
We can go even further:
public static (IEnumerable<T> XS, IEnumerable<T> YS, IEnumerable<T> ZS) Trifurcate<T>(this IEnumerable<T> source, Func<T, bool> predicate1, Func<T, bool> predicate2)
{
var lookup = source.ToLookup(x =>
{
if (predicate1(x))
return 1;
if (predicate2(x))
return 2;
return 3;
});
return (lookup[1], lookup[2], lookup[3]);
}
The order of predicates matters with this one. If you pass n => n > 5 and n => n > 100 in that order for example, the second collection will always be empty.
One might even have an itch to come up with a version of this that would work with a variable number of predicates(I know I did) but as far as I know that's not possible with tuple return values in C#.

Categories

Resources