Reputation: 18764
I have a list of strings and I want to pick the string that contains the minimum count of ~
or >
or |
All the solutions I have come up with gives the minimum count instead of the string that has the minimum count .
Any ideas ?
What I have so far:
private string FilterShortestPath(List<string> paths)
{
return paths.Min(x => x.Count(c => c == '~' || c == '>' || c == '|'));
}
Upvotes: 0
Views: 1018
Reputation: 326
The following code
var badChars = new[] { '~', '>', '|' };
var strings = new[]
{
"I ~ love >> cookies |||",
"I >>>> love pizza ~",
"I hate tildas~~"
};
var grouped = from str in strings
let count = str.Count(c => badChars.Contains(c))
orderby count ascending
select new { Text = str, Count = count };
var tuple = grouped.First();
Console.WriteLine("Text is '{0}' with {1} occurences", tuple.Text, tuple.Count);
should do the thing for a single minimum. However, since you possibly need multiple minimums, the following one should help:
var badChars = new[] { '~', '>', '|' };
var strings = new[]
{
"I ~ love >> cookies |||",
"I >>>> love pizza ~",
"I hate tildas~~",
"Bars are also | | bad"
};
var grouped = (from str in strings
let count = str.Count(c => badChars.Contains(c))
orderby count ascending
select new { Text = str, Count = count }).ToList();
int min = grouped[0].Count;
var minimums = grouped.TakeWhile(g => g.Count == min);
foreach(var g in minimums)
Console.WriteLine("Text is '{0}' with {1} occurences", g.Text, g.Count);
Upvotes: 0
Reputation: 477676
You can do this using the IEnumerable<T>.Count
method with a predicate and that predicate checks if the char is stored in a collection of accepted characters.
String[] strings = new String[] {"<todo|>","~foo"};
HashSet<char> accept = new HashSet<char>(new char[] {'~','>','|'});
strings.ArgMin(x => x.Count(accept.Contains));
With:
public static TS ArgMin<TS,TF> (this IEnumerable<TS> source, Func<TS,TF> f) where TF : IComparable<TF> {
if(source == null) {
throw new ArgumentException("Source must be effective.");
}
IEnumerator<TS> en = source.GetEnumerator();
if(!en.MoveNext()) {
throw new ArgumentException("You need to provide at least one argument.");
}
TS x0 = en.Current, xi;
TF f0 = f(x0), fi;
while(en.MoveNext()) {
xi = en.Current;
fi = f(xi);
if(f0.CompareTo(fi) > 0) {
f0 = fi;
x0 = xi;
}
}
return x0;
}
ArgMin
returns the item in the IEnumerable<T>
that resulted after applying f
in the smallest value. In case multiple values result in the same value, the first is selected. If no items are provided, or the list is not effective, an exception is thrown.
Upvotes: 0
Reputation: 143373
If i got your task correctly:
var strings = new []{"~~~~", "wwqewqe>", "||"};
var chars = new[] {'~', '|', '>'};
var result = strings.OrderBy(s => s.Count(c => chars.Contains(c))).First();
The problem with you solution is that Enumerable.Min
accepts selector and not a condition. If you want to use it with condition you can look here for options
Upvotes: 1
Reputation: 460288
If you want to find all strings with the min-count you could use Where
:
private IEnumerable<string> FilterShortestPath(IEnumerable<string> paths, params char[] chars)
{
if(paths == null || !paths.Any())
return Enumerable.Empty<string>();
int minCount = paths.Min(str => (str ?? "").Count(chars.Contains));
return paths.Where(str => (str ?? "").Count(chars.Contains) == minCount);
}
Usage:
var minCountStrings = FilterShortestPath(list, '~', '>' , '|');
Note that you can use this method also if you only want one due to it's deferred execution. Then you just have to use FilterShortestPath(list, '~', '>' , '|').First()
.
Upvotes: 2