Linq OrderBy against specific values - c#

Is there a way in Linq to do an OrderBy against a set of values (strings in this case) without knowing the order of the values?
Consider this data:
A
B
A
C
B
C
D
E
And these variables:
string firstPref, secondPref, thirdPref;
When the values are set like so:
firstPref = 'A';
secondPref = 'B';
thirdPref = 'C';
Is it possible to order the data like so:
A
A
B
B
C
C
D
E

If you put your preferences into a list, it might become easier.
List<String> data = new List<String> { "A","B","A","C","B","C","D","E" };
List<String> preferences = new List<String> { "A","B","C" };
IEnumerable<String> orderedData = data.OrderBy(
item => preferences.IndexOf(item));
This will put all items not appearing in preferences in front because IndexOf() returns -1. An ad hoc work around might be reversing preferences and order the result descending. This becomes quite ugly, but works.
IEnumerable<String> orderedData = data.OrderByDescending(
item => Enumerable.Reverse(preferences).ToList().IndexOf(item));
The solution becomes a bit nicer if you concat preferences and data.
IEnumerable<String> orderedData = data.OrderBy(
item => preferences.Concat(data).ToList().IndexOf(item));
I don't like Concat() and ToList() in there. But for the moment I have no really good way around that. I am looking for a nice trick to turn the -1 of the first example into a big number.

In addition to #Daniel Brückner answer and problem defined at the end of it:
I don't like Concat() and ToList() in there. But for the moment I have no really >good way around that. I am looking for a nice trick to turn the -1 of the first >example into a big number.
I think that the solution is to use a statement lambda instead of an expression lambda.
var data = new List<string> { "corge", "baz", "foo", "bar", "qux", "quux" };
var fixedOrder = new List<string> { "foo", "bar", "baz" };
data.OrderBy(d => {
var index = fixedOrder.IndexOf(d);
return index == -1 ? int.MaxValue : index;
});
The ordered data is:
foo
bar
baz
corge
qux
quux

Put the preferred values in a dictionary. Looking up keys in a dictionary is a O(1) operation compared to finding values in a list which is a O(n) operation, so it scales much better.
Create a sort string for each preferred value so that they are placed before the other values. For the other values the value itself will be used as sorting string so that they are actually sorted. (Using any arbitrary high value would only place them at the end of the list unsorted).
List<string> data = new List<string> {
"E", "B", "D", "A", "C", "B", "A", "C"
};
var preferences = new Dictionary<string, string> {
{ "A", " 01" },
{ "B", " 02" },
{ "C", " 03" }
};
string key;
IEnumerable<String> orderedData = data.OrderBy(
item => preferences.TryGetValue(item, out key) ? key : item
);

Combined all answers (and more) into a generic LINQ extension supporting caching which handles any data type, can be case-insensitive and allows to be chained with pre- and post-ordering:
public static class SortBySample
{
public static BySampleSorter<TKey> Create<TKey>(IEnumerable<TKey> fixedOrder, IEqualityComparer<TKey> comparer = null)
{
return new BySampleSorter<TKey>(fixedOrder, comparer);
}
public static BySampleSorter<TKey> Create<TKey>(IEqualityComparer<TKey> comparer, params TKey[] fixedOrder)
{
return new BySampleSorter<TKey>(fixedOrder, comparer);
}
public static IOrderedEnumerable<TSource> OrderBySample<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, BySampleSorter<TKey> sample)
{
return sample.OrderBySample(source, keySelector);
}
public static IOrderedEnumerable<TSource> ThenBySample<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, BySampleSorter<TKey> sample)
{
return sample.ThenBySample(source, keySelector);
}
}
public class BySampleSorter<TKey>
{
private readonly Dictionary<TKey, int> dict;
public BySampleSorter(IEnumerable<TKey> fixedOrder, IEqualityComparer<TKey> comparer = null)
{
this.dict = fixedOrder
.Select((key, index) => new KeyValuePair<TKey, int>(key, index))
.ToDictionary(kv => kv.Key, kv => kv.Value, comparer ?? EqualityComparer<TKey>.Default);
}
public BySampleSorter(IEqualityComparer<TKey> comparer, params TKey[] fixedOrder)
: this(fixedOrder, comparer)
{
}
public IOrderedEnumerable<TSource> OrderBySample<TSource>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
return source.OrderBy(item => this.GetOrderKey(keySelector(item)));
}
public IOrderedEnumerable<TSource> ThenBySample<TSource>(IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
return source.CreateOrderedEnumerable(item => this.GetOrderKey(keySelector(item)), Comparer<int>.Default, false);
}
private int GetOrderKey(TKey key)
{
int index;
return dict.TryGetValue(key, out index) ? index : int.MaxValue;
}
}
Sample usage using LINQPad-Dump():
var sample = SortBySample.Create(StringComparer.OrdinalIgnoreCase, "one", "two", "three", "four");
var unsorted = new[] {"seven", "six", "five", "four", "THREE", "tWo", "One", "zero"};
unsorted
.OrderBySample(x => x, sample)
.ThenBy(x => x)
.Dump("sorted by sample then by content");
unsorted
.OrderBy(x => x.Length)
.ThenBySample(x => x, sample)
.Dump("sorted by length then by sample");

Danbrucs solution is more elegant, but here is a solution using a custom IComparer. This might be useful if you need more advanced conditions for your sort ordering.
string[] svals = new string[] {"A", "B", "A", "C", "B", "C", "D", "E"};
List<string> list = svals.OrderBy(a => a, new CustomComparer()).ToList();
private class CustomComparer : IComparer<string>
{
private string firstPref = "A";
private string secondPref = "B";
private string thirdPref = "C";
public int Compare(string x, string y)
{
// first pref
if (y == firstPref && x == firstPref)
return 0;
else if (x == firstPref && y != firstPref)
return -1;
else if (y == firstPref && x != firstPref)
return 1;
// second pref
else if (y == secondPref && x == secondPref)
return 0;
else if (x == secondPref && y != secondPref)
return -1;
else if (y == secondPref && x != secondPref)
return 1;
// third pref
else if (y == thirdPref && x == thirdPref)
return 0;
else if (x == thirdPref && y != thirdPref)
return -1;
else
return string.Compare(x, y);
}
}

Yes, you must implement your own IComparer<string> and then pass it in as the second argument of LINQ's OrderBy method.
An example can be found here:
Ordering LINQ results

Not really efficient for large lists but fairly easy to read:
public class FixedOrderComparer<T> : IComparer<T>
{
private readonly T[] fixedOrderItems;
public FixedOrderComparer(params T[] fixedOrderItems)
{
this.fixedOrderItems = fixedOrderItems;
}
public int Compare(T x, T y)
{
var xIndex = Array.IndexOf(fixedOrderItems, x);
var yIndex = Array.IndexOf(fixedOrderItems, y);
xIndex = xIndex == -1 ? int.MaxValue : xIndex;
yIndex = yIndex == -1 ? int.MaxValue : yIndex;
return xIndex.CompareTo(yIndex);
}
}
Usage:
var orderedData = data.OrderBy(x => x, new FixedOrderComparer<string>("A", "B", "C"));
Note: Array.IndexOf<T>(....) uses EqualityComparer<T>.Default to find the target index.

I use these. I like the IEnumerable overload for cleanliness, but the priority map version should have better performance on repeated calls.
public static IEnumerable<T> OrderByStaticList<T>(this IEnumerable<T> items, IReadOnlyDictionary<T, double> priorityMap)
{
return items.OrderBy(x => priorityMap.GetValueOrDefault(x, double.MaxValue));
}
public static IEnumerable<T> OrderByStaticList<T>(this IEnumerable<T> items, IEnumerable<T> preferenceOrder)
{
int priority = 0;
var priorityMap = preferenceOrder.ToDictionary(x => x, x => (double) priority++);
return OrderByStaticList(items, priorityMap);
}
[TestMethod]
public void PriorityMap_DeterminesSort()
{
var map = new Dictionary<char, double>()
{
{'A', 1},
{'B', 7},
{'C', 3},
{'D', 42},
{'E', -1},
};
Assert.AreEqual("EACBD", new string("ABCDE".OrderByStaticList(map).ToArray()));
}
[TestMethod]
public void PriorityMapMissingItem_SortsLast()
{
var map = new Dictionary<char, double>()
{
{'A', 1},
{'B', 7},
{'D', 42},
{'E', -1},
};
Assert.AreEqual("EABDC", new string("ABCDE".OrderByStaticList(map).ToArray()));
}
[TestMethod]
public void OrderedList_DeterminesSort()
{
Assert.AreEqual("EACBD", new string("ABCDE".OrderByStaticList("EACBD").ToArray()));
}
[TestMethod]
public void OrderedListMissingItem_SortsLast()
{
Assert.AreEqual("EABDC", new string("ABCDE".OrderByStaticList("EABD").ToArray()));
}

Related

Permutations with repetition

If you can find a better title, please edit.
I will start by saying I've looked at several q&a's on this topic, mainly this one and this article without having found a way to do this:
Given the word "HALLOWEEN" I would like to find all permutations and combinations for all lengths. The first thing I tried was iterating through the below code giving it length of 1 to begin with and continuing until reaching the length of the word (9).
public static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] {t});
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] {t2}));
}
This gave me unexpected results as the double 'E' and 'L's were omitted, leaving the final set short.
A simpler example could be 'MOM' {M,O,M} where the final set of outcomes would be:
M
O
MO
OM
MM
MOM
MMO
OMM
Notice that I want to see both 'M's as available, but I don't want to see "MMM" as a result. "MOM" would appear twice in the result due to leaving original order (1,2,3) and swapping positions 1 and 3 (3,2,1) would both result in 'M','O','M' but this character sequence only appears once is the result list (which can be done by a string comparison)
Again, with set {1,1,2,3} I would expect to see:
{1,1}
but NOT {2,2} or {3,3}
Here's another solution that should be clear and easily understandable:
public static IEnumerable<string> GetPermutations(string input)
{
if (string.IsNullOrEmpty(input))
{
return new List<string>();
}
var length = input.Length;
var indices = Enumerable.Range(0, length).ToList();
var permutationsOfIndices = GetNumericalPermutations(indices, length);
var permutationsOfInput = permutationsOfIndices.Select(x => new string(x.Select(y => input[y]).ToArray()))
.Distinct();
return permutationsOfInput;
}
private static List<List<int>> GetNumericalPermutations(List<int> values, int maxLength)
{
if (maxLength == 1)
{
return values.Select(x => new List<int>{x}).ToList();
}
else
{
var permutations = GetNumericalPermutations(values, maxLength - 1);
foreach (var index in values)
{
var newPermutations = permutations.Where(x => !x.Contains(index))
.Select(x => x.Concat(new List<int> { index }))
.Where(x => !permutations.Any(y => y.SequenceEqual(x)))
.Select(x => x.ToList())
.ToList();
permutations.AddRange(newPermutations);
}
return permutations;
}
}
For example, the output for "MOM" is:
M
O
OM
MM
MO
MMO
OMM
MOM
I suggest looking at the permutations of the letter positions 0,1,2,3,4,etc mapping those to letters, and then eliminating the duplicates.
Without changing the GetPermutations function, I added another function to get the permutations of the letter positions, map those result to character strings and then eliminate the duplicates.
public void PermutationsTestMethod()
{
GetPermutationsOfString("MOM").ForEach(v => Debug.Print(v));
}
public List<string> GetPermutationsOfString(string value)
{
var resultList = new List<string>();
for (var i = 1; i <= value.Length; i++)
{
var permutations = GetPermutations(Enumerable.Range(0, value.Length), i);
resultList.AddRange(
permutations
.Select(v => new string(v.Select(z => value[z]).ToArray()))
.Distinct()
);
}
return resultList;
}
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
This works fine:
Func<string, IEnumerable<string>> getAllSubsets = null;
getAllSubsets = x =>
(x == null || !x.Any())
? Enumerable.Empty<string>()
: (x.Length > 1
? getAllSubsets(x.Substring(1))
.SelectMany(y => new [] { y, x.Substring(0, 1) + y })
: new [] { "", x.Substring(0, 1) });
So given getAllSubsets("ABC") I get:
"", "A", "B", "AB", "C", "AC", "BC", "ABC"
And, for your "MOM" example I get:
"", "M", "O", "MO", "M", "MM", "OM", "MOM"
It would be trivial to filter out the empty string and duplicate values if need be, but as it stands it strictly produces all subsets.
I think it is generally better try to avoid generating and eliminating permutations. Text like "aaaaaaaaaaaaaaab" can generate really big amount of duplications.
public static IEnumerable<IEnumerable<T>>
GetPermutationsInner<T>(IEnumerable<IGrouping<T, T>> groupedList, int length)
{
if (length == 1) return groupedList.Select(t => new T[] { t.Key });
return GetPermutationsInner<T>(groupedList, length - 1)
.SelectMany(t => groupedList
.Where(e => t.Count(w => w.Equals(e.Key)) < e.Count())
.Select(s => s.Key),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
public static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list)
{
var resultList = new List<IEnumerable<T>>();
for (int i = 1; i <= list.Count(); ++i)
{
resultList.AddRange(GetPermutationsInner<T>(list.GroupBy(g => g), i));
}
return resultList;
}

All possible collections of present giving permutations

So Christmas is coming up and every year my family pulls names from a hat for who should buy present for who, and invariably there are concerns, mostly around spouses buying presents for each other.
Assume the families are like so:
List<List<string>> families = new List<List<string>> ()
{
new List<string>() { "A1", "A2" },
new List<string>() { "B1", "B2" },
new List<string>() { "C1", "C2" },
new List<string>() { "D1", "D2" }
};
People in family A can't buy for the others in their family, likewise for families B, C, D.
We can easily get a family from a given person with:
public static IEnumerable<string> FamilyOf(this List<List<string>> families, string person)
{
return families.Where(family => family.Contains(person)).First();
}
... and we can get all valid pairs with:
var everyone = families.SelectMany(family => family);
var pairs = from giver in everyone
from receiver in everyone
where !families.FamilyOf(giver).Contains(receiver)
select Tuple.Create(giver, receiver);
How can I turn this into the possible collections of permutations of valid givers/receivers that includes everyone? From that I'll just select a random collection.
I wrote a bit of code to solve your problem, but it can sometimes throw an exception, when it gets a bit "unlucky" with picking the pairs. For example if the algorithm pairs A1B2 B1C2 C1A2 -> so only D1 and D2 are left, which causes an exception since it doesn't meet your pairing requirement anymore.
Anyway here is the code, which you might want to expand to prevent it from throwing an exception:
var everyone = families.SelectMany(family => family).ToList();
everyone.Shuffle();
var randPairs = families.SelectMany(family => family)
.Select(p => new {
Giver = p,
Receiver = everyone.PopRandom(x => !p.Contains(x[0]))
});
And the two extension methods for IList:
public static T PopRandom<T>(this IList<T> list, Func<T, bool> predicate)
{
var predicatedList = list.Where(x => predicate(x));
int count = predicatedList.Count();
if (count == 0)
{
throw new Exception();
}
T item = predicatedList.ElementAt(Rand.Next(count));
while (item != null && !predicate(item))
{
item = predicatedList.ElementAt(Rand.Next(list.Count));
}
list.Remove(item);
return item;
}
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = Rand.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}

LINQ swap columns into rows

Is there a fancy LINQ expression that could allow me to do the following in a much more simpler fashion. I have a List<List<double>>, assuming the List are columns in a 2d matrix, I want to swap the list of columns into a list of rows. I have the following obvious solution:
int columns = 5;
var values; // assume initialised as List<List<double>>()
var listOfRows = new List<List<double>>();
for (int i = 0; i < columns ; i++)
{
List<double> newRow = new List<double>();
foreach (List<double> value in values)
{
newRow.Add(value[i]);
}
listOfRows.Add(newRow);
}
You could LINQify the inner loop pretty easily:
vector.AddRange(values.Select(value => value[i]));
Whether or not that improves the readability is left entirely up to you!
Here's a Linq expression that would do what you want - looking at it I'd personally stick with the nested foreach loops though - much easier to read:
var columnList= new List<List<double>>();
columnList.Add(new List<double>() { 1, 2, 3 });
columnList.Add(new List<double>() { 4, 5, 6 });
columnList.Add(new List<double>() { 7, 8, 9 });
columnList.Add(new List<double>() { 10, 11, 12 });
int columnCount = columnList[0].Count;
var rowList = columnList.SelectMany(x => x)
.Select((x, i) => new { V = x, Index = i })
.GroupBy(x => (x.Index + 1) % columnCount)
.Select(g => g.Select( x=> x.V).ToList())
.ToList();
This example also would only work on a matrix with a fixed column count. Basically it's flattening the matrix into a list, then creating the list of rows by grouping by the index of the element in the list modulo the column count.
Edit:
A different approach, much closer to a nested loop and probably similar performance besides the overhead.
int columnCount = columnList[0].Count;
int rowCount = columnList.Count;
var rowList = Enumerable.Range(0, columnCount)
.Select( x => Enumerable.Range(0, rowCount)
.Select(y => columnList[y][x])
.ToList())
.ToList();
var inverted = Enumerable.Range(0, columnCount)
.Select(index => columnList.Select(list => list[index]));
In short, we enumerate the column index from a range and use it to collect the nth element of each list.
Please note that you'll need to check that every list has the same number of columns.
Here's one that works for rectangular (non-ragged) matrices. The C# code here works cut-and-paste into LinqPad, a free, interactive C# programming tool.
I define a postfix operator (that is, an extension method) "Transpose." Use the operator as follows:
var rand = new Random();
var xss = new [] {
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
};
xss.Dump("Original");
xss.Transpose().Dump("Transpose");
resulting in something like this:
Original
0.843094345109116
0.981432441613373
0.649207864724662
0.00594645645746331
0.378864820291691
0.336915332515219
Transpose
0.843094345109116
0.649207864724662
0.378864820291691
0.981432441613373
0.00594645645746331
0.336915332515219
The gist of the implementation of this operator is the following
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> xss)
{
var heads = xss.Heads();
var tails = xss.Tails();
var empt = new List<IEnumerable<T>>();
if (heads.IsEmpty())
return empt;
empt.Add(heads);
return empt.Concat(tails.Transpose());
}
Here is the full implementation, with some lines commented out that you can uncomment to monitor how the function works.
void Main()
{
var rand = new Random();
var xss = new [] {
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
};
xss.Dump("Original");
xss.Transpose().Dump("Transpose");
}
public static class Extensions
{
public static IEnumerable<T> Heads<T>(this IEnumerable<IEnumerable<T>> xss)
{
Debug.Assert(xss != null);
if (xss.Any(xs => xs.IsEmpty()))
return new List<T>();
return xss.Select(xs => xs.First());
}
public static bool IsEmpty<T>(this IEnumerable<T> xs)
{
return xs.Count() == 0;
}
public static IEnumerable<IEnumerable<T>> Tails<T>(this IEnumerable<IEnumerable<T>> xss)
{
return xss.Select(xs => xs.Skip(1));
}
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> xss)
{
// xss.Dump("xss in Transpose");
var heads = xss.Heads()
// .Dump("heads in Transpose")
;
var tails = xss.Tails()
// .Dump("tails in Transpose")
;
var empt = new List<IEnumerable<T>>();
if (heads.IsEmpty())
return empt;
empt.Add(heads);
return empt.Concat(tails.Transpose())
// .Dump("empt")
;
}
}
I am combining some of the answers above, which sometimes had columns and rows inverted form the original answer or from the convention I am used to : row refers to the first index and column to the inner ( second) index. e.g. values[row][column]
public static List<List<T>> Transpose<T>(this List<List<T>> values)
{
if (values.Count == 0 || values[0].Count == 0)
{
return new List<List<T>>();
}
int ColumnCount = values[0].Count;
var listByColumns = new List<List<T>>();
foreach (int columnIndex in Enumerable.Range(0, ColumnCount))
{
List<T> valuesByColumn = values.Select(value => value[columnIndex]).ToList();
listByColumns.Add(valuesByColumn);
}
return listByColumns;
}
Actually the word row and column is just our convention of thinking about the data in rows and columns , and sometimes adds more confusion than solving them.
We are actually just swapping the inner index for the outer index. (or flipping the indexes around). So one could also just define the following extension method. . Again I borrowed from above solutions, just put it into something I find readable and fairly compact.
Checks that the inner lists are of equal sized are required.
public static List<List<T>> InsideOutFlip<T>(this List<List<T>> values)
{
if (values.Count == 0 || values[0].Count == 0)
{
return new List<List<T>>();
}
int innerCount = values[0].Count;
var flippedList = new List<List<T>>();
foreach (int innerIndex in Enumerable.Range(0, innerCount))
{
List<T> valuesByOneInner = values.Select(value => value[innerIndex]).ToList();
flippedList.Add(valuesByOneInner);
}
return flippedList;
}

Select integers from string[], and return int[]

I have an array string[] with values that are mostly convertible to integers.
var values = new [] {"", "1", "2", "a", "3"};
I need to convert values to an array of integers, discarding any items that aren't convertible. So I end up with
var numbers = new [] {1, 2, 3};
What would be the most efficient (quickest and clean code) way to do this?
var numbers = values.Select(
s => {
int n;
if (!int.TryParse((s ?? string.Empty), out n))
{
return (int?)null;
}
return (int?)n;
}
)
.Where(n => n != null)
.Select(n => n.Value)
.ToArray();
Here is my take on this. It uses a separate method but cleanly expresses the conditional parse without using nullable values:
private static IEnumerable<int> ParseInt32s(IEnumerable<string> value)
{
foreach(var value in values)
{
int n;
if(Int32.TryParse(value, out n))
{
yield return n;
}
}
}
Usage:
string[] values;
var parsedValues = ParseInt32s(values).ToArray();
I personally use an extension method that's a little different from what others have posted so far. It specifically uses a custom WeakConverter<TSource, TResult> delegate to avoid the issue in Ron's answer of calling ToString() on every object, while maintaining genericity unlike Jared's answer (though I will concede that sometimes trying to make everything generic is overdoing it -- but in this case, the added effort is really not much for what I consider a significant benefit in terms of reusability).
public delegate bool WeakConverter<TSource, TResult>(TSource source, out TResult result);
public static IEnumerable<TResult> TryConvertAll<TSource, TResult>(this IEnumerable<TSource> source, WeakConverter<TSource, TResult> converter)
{
foreach (TSource original in source)
{
TResult converted;
if (converter(original, out converted))
{
yield return converted;
}
}
}
With this in place, you can convert a string[] to an int[] quite simply and robustly (no double-parsing necessary):
string[] strings = new[] { "1", "2", "abc", "3", "", "123" };
int[] ints = strings.TryConvertAll<string, int>(int.TryParse).ToArray();
foreach (int x in ints)
{
Console.WriteLine(x);
}
Output:
1
2
3
123
This can be done in a single linq statement without having to do the parse twice:
var numbers = values
.Select(c => { int i; return int.TryParse(c, out i) ? i : (int?)null; })
.Where(c => c.HasValue)
.Select(c => c.Value)
.ToArray();
EDIT: Updated to not use try/catch, since StackOverflow users have pointed out that it's slow.
Try this.
var values = new[] { "", "1", "2", "a", "3" };
List<int> numeric_list = new List();
int num_try = 0;
foreach (string string_value in values)
{
if (Int32.TryParse(string_value, out num_try) {
numeric_list.Add(num_try);
}
/* BAD PRACTICE (as noted by other StackOverflow users)
try
{
numeric_list.Add(Convert.ToInt32(string_value));
}
catch (Exception)
{
// Do nothing, since we want to skip.
}
*/
}
return numeric_list.ToArray();
You could try the following
public static IEnumerable<int> Convert(this IEnumerable<string> enumerable) {
Func<string,int?> convertFunc = x => {
int value ;
bool ret = Int32.TryParse(x, out value);
return ret ? (int?)value : null;
};
return enumerable
.Select(convertFunc)
.Where(x => x.HasValue)
.Select(x => x.Value);
}
This can easily then be converted to an array.
var numbers = values.Convert().ToArray();
With some straightforward LINQ:
var numbers = values.Where(x => { int num = 0; return Int32.TryParse(x, out num); })
.Select(num => Int32.Parse(num));
Notably this converts every string twice. Doing this imperatively you'll lose some clarity but gain some speed (as an IEnumerable extension):
public static IEnumerable<int> TryCastToInt<T>(this IEnumerable<T> values)
int num = 0;
foreach (object item in values) {
if (Int32.TryParse(item.ToString(), num)) {
yield return num;
}
}
}
int n;
var values = new[] { "", "1", "2", "a", "3" };
var intsonly = values.Where (v=> Int32.TryParse(v, out n)).Select(x => Int32.Parse(x));
var numbers = values
.Where(x => !String.IsNullOrEmpty(x))
.Where(x => x.All(Char.IsDigit))
.Select(x => Convert.ToInt32(x))
.ToArray();

Use Linq to find consecutively repeating elements

Let's assume I have a list with objects of type Value. Value has a Name property:
private List<Value> values = new List<Value> {
new Value { Id = 0, Name = "Hello" },
new Value { Id = 1, Name = "World" },
new Value { Id = 2, Name = "World" },
new Value { Id = 3, Name = "Hello" },
new Value { Id = 4, Name = "a" },
new Value { Id = 5, Name = "a" },
};
Now I want to get a list of all "repeating" values (elements where the name property was identical with the name property of the previous element).
In this example I want a list with the two elements "world" and "a" (id = 2 and 5) to be returned.
Is this event possible with linq?
Of course I could so smth. like this:
List<Value> tempValues = new List<Value>();
String lastName = String.Empty();
foreach (var v in values)
{
if (v.Name == lastName) tempValues.Add(v);
lastName = v.Name;
}
but since I want to use this query in a more complex context, maybe there is a "linqish" solution.
There won't be anything built in along those lines, but if you need this frequently you could roll something bespoke but fairly generic:
static IEnumerable<TSource> WhereRepeated<TSource>(
this IEnumerable<TSource> source)
{
return WhereRepeated<TSource,TSource>(source, x => x);
}
static IEnumerable<TSource> WhereRepeated<TSource, TValue>(
this IEnumerable<TSource> source, Func<TSource, TValue> selector)
{
using (var iter = source.GetEnumerator())
{
if (iter.MoveNext())
{
var comparer = EqualityComparer<TValue>.Default;
TValue lastValue = selector(iter.Current);
while (iter.MoveNext())
{
TValue currentValue = selector(iter.Current);
if (comparer.Equals(lastValue, currentValue))
{
yield return iter.Current;
}
lastValue = currentValue;
}
}
}
}
Usage:
foreach (Value value in values.WhereRepeated(x => x.Name))
{
Console.WriteLine(value.Name);
}
You might want to think about what to do with triplets etc - currently everything except the first will be yielded (which matches your description), but that might not be quite right.
You could implement a Zip extension, then Zip your list with .Skip(1) and then Select the rows that match.
This should work and be fairly easy to maintain:
values
.Skip(1)
.Zip(items, (first,second) => first.Name==second.Name?first:null)
.Where(i => i != null);
The slight disadvantage of this method is that you iterate through the list twice.
I think this would work (untested) -- this will give you both the repeated word and it's index. For multiple repeats you could traverse this list and check for consecutive indices.
var query = values.Where( (v,i) => values.Count > i+1 && v == values[i+1] )
.Select( (v,i) => new { Value = v, Index = i } );
Here's another simple approach that should work if the IDs are always sequential as in your sample:
var data = from v2 in values
join v1 in values on v2.Id equals v1.Id + 1
where v1.Name == v2.Name
select v2;
I know this question is ancient but I was just working on the same thing so ....
static class utils
{
public static IEnumerable<T> FindConsecutive<T>(this IEnumerable<T> data, Func<T,T,bool> comparison)
{
return Enumerable.Range(0, data.Count() - 1)
.Select( i => new { a=data.ElementAt(i), b=data.ElementAt(i+1)})
.Where(n => comparison(n.a, n.b)).Select(n => n.a);
}
}
Should work for anything - just provide a function to compare the elements
You could use the GroupBy extension to do this.
Something like this
var dupsNames =
from v in values
group v by v.Name into g
where g.Count > 1 // If a group has only one element, just ignore it
select g.Key;
should work. You can then use the results in a second query:
dupsNames.Select( d => values.Where( v => v.Name == d ) )
This should return a grouping with key=name, values = { elements with name }
Disclaimer: I did not test the above, so I may be way off.

Categories