What is c# alternative of splice in c++ - c#

I have a c++ code and trying to write in C#
I couldn't figure out best alternative to splice in c#
Also C++ has a 'find' too to work on map,which I want to implement in C# on dictionary

In your C++ example, you show:
statement_tokens.splice(statement_tokens.begin(), tokens, tokens.begin(), next_sc);
From what I understand (documentation), this overload takes an insert position, a list (of the same type), and the first (inclusive) and last (exclusive) indexes of a range to splice into the insert position, and then inserts this range into the original list.
Update: AND it removes the items from the other list. I just added that functionality.
If this is correct, then the following extension method should work:
List Extension Method (check the end of this answer for other overloads of this method)
public static class ListExtensions
{
public static void Splice<T>(this List<T> list, int insertAtIndex, List<T> items,
int first, int last)
{
if (items == null) return;
insertAtIndex = Math.Min(list.Count, Math.Max(0, insertAtIndex));
first = Math.Min(items.Count - 1, Math.Max(0, first));
last = Math.Min(items.Count, Math.Max(1, last));
if (first >= last) return;
list.InsertRange(insertAtIndex, items.GetRange(first, last - first));
items.RemoveRange(first, last - first);
}
}
Update 2: Now, it looks like you're missing another extension method for std::find_if, which returns the index of a list item in a specified range, based on a method that returns true if the item meets some condition. So let's add the following method to the ListExtensions class above:
public static int FindIf<T>(this List<T> list, int start, int end, Func<T, bool> method)
{
if (method == null) return end;
if (!list.Any(method)) return end;
start = Math.Min(list.Count - 1, Math.Max(0, start));
end = Math.Min(list.Count, Math.Max(1, end));
if (start >= end) return end;
var range = list.GetRange(start, end - start);
var index = range.IndexOf(list.First(method));
if (index < start) return end;
return index;
}
Notice that one of the arguments to this method is a function that takes an item of type T and returns a bool. This will be a simple method that checks if the string value of our token is a semicolon:
static bool TokenIsSemicolon(EvlToken token)
{
return (token != null && token.Str == ";");
}
Now, you may notice that I referenced token.Str. This is from the EvlToken class, which was created to mimic the C++ struct:
class EvlToken
{
public enum TokenType { Name, Number, Single }
public TokenType Type { get; set; }
public string Str { get; set; }
public int LineNo { get; set; }
}
Now we can finish the conversion of the original method, calling our FindIf and Splice extension methods:
static bool MoveTokensToStatement(List<EvlToken> statementTokens, List<EvlToken> tokens)
{
if (statementTokens == null || statementTokens.Count > 0) return false;
if (tokens == null || tokens.Count == 0) return false;
int nextSemiColon = tokens.FindIf(0, tokens.Count, TokenIsSemicolon);
if (nextSemiColon == tokens.Count)
{
Console.WriteLine("Looked for ';' but reached the end of the file.");
return false;
}
++nextSemiColon;
statementTokens.Splice(0, tokens, 0, nextSemiColon);
return true;
}
Additional Overloads
For completeness, here is the extensions class with the other two overloads mentioned in the documentation:
public static class ListExtensions
{
/// <summary>
/// Transfers all elements from 'items' into 'this' at the specified index
/// </summary>
/// <typeparam name="T">The type of items in the list</typeparam>
/// <param name="list">'this' instance</param>
/// <param name="insertAtIndex">The index to insert the items</param>
/// <param name="items">The list to transfer the items from</param>
public static void Splice<T>(this List<T> list, int insertAtIndex,
List<T> items)
{
if (items == null) return;
list.Splice(insertAtIndex, items, 0, items.Count);
}
/// <summary>
/// Transfers the element at 'itemIndex' from 'items'
/// into 'this' at the specified index
/// </summary>
/// <typeparam name="T">The type of items in the list</typeparam>
/// <param name="list">'this' instance</param>
/// <param name="insertAtIndex">The index to insert the item</param>
/// <param name="items">The list to transfer the item from</param>
/// <param name="itemIndex">The index of the item to transfer</param>
public static void Splice<T>(this List<T> list, int insertAtIndex,
List<T> items, int itemIndex)
{
list.Splice(insertAtIndex, items, itemIndex, itemIndex + 1);
}
/// <summary>
/// Transfers the specified range of elements from 'items'
/// into 'this' at the specified index
/// </summary>
/// <typeparam name="T">The type of items in the list</typeparam>
/// <param name="list">'this' instance</param>
/// <param name="insertAtIndex">The index to insert the item</param>
/// <param name="items">The list to transfer the item from</param>
/// <param name="first">The index of the first item in the range</param>
/// <param name="last">The exclusive index of the last item in the range</param>
public static void Splice<T>(this List<T> list, int insertAtIndex, List<T> items,
int first, int last)
{
if (items == null) return;
insertAtIndex = Math.Min(list.Count, Math.Max(0, insertAtIndex));
first = Math.Min(items.Count - 1, Math.Max(0, first));
last = Math.Min(items.Count, Math.Max(1, last));
if (first >= last) return;
list.InsertRange(insertAtIndex, items.GetRange(first, last - first));
items.RemoveRange(first, last - first);
}
/// <summary>
/// Searches for the first item in the specified range that "method" returns true for
/// </summary>
/// <typeparam name="T">The type of items in the list</typeparam>
/// <param name="list">'this' instance</param>
/// <param name="start">The index of the first item in the range</param>
/// <param name="end">The exclusive index of the last item in the range</param>
/// <param name="method">A method which takes type 'T' and returns a bool</param>
/// <returns>The index of the item, if found, otherwise 'end'</returns>
public static int FindIf<T>(this List<T> list, int start, int end, Func<T, bool> method)
{
if (method == null) return end;
if (!list.Any(method)) return end;
start = Math.Min(list.Count - 1, Math.Max(0, start));
end = Math.Min(list.Count, Math.Max(1, end));
if (start >= end) return end;
var range = list.GetRange(start, end - start);
var index = range.IndexOf(list.First(method));
if (index < start) return end;
return index;
}
}
Example Usage
Here's an example using a list of EvlTokens, and then calling MoveTokensToStatement twice:
private static void Main()
{
var tokens = new List<EvlToken>
{
new EvlToken {LineNo = 3, Str = "int", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 3, Str = "x", Type = EvlToken.TokenType.Name},
new EvlToken {LineNo = 3, Str = "=", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 3, Str = "1", Type = EvlToken.TokenType.Number},
new EvlToken {LineNo = 3, Str = "+", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 3, Str = "5", Type = EvlToken.TokenType.Number},
new EvlToken {LineNo = 3, Str = ";", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = "Console", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = ".", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = "WriteLine", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = "(", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = "Hello World", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = ")", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = ";", Type = EvlToken.TokenType.Single}
};
var statementTokens = new List<EvlToken>();
MoveTokensToStatement(statementTokens, tokens);
Console.WriteLine("Here is the result of calling 'MoveTokensToStatement' the first time:");
Console.WriteLine(string.Join(" ", statementTokens.Select(t => t.Str)));
statementTokens.Clear();
MoveTokensToStatement(statementTokens, tokens);
Console.WriteLine("\nHere is the result of calling 'MoveTokensToStatement' the second time:");
Console.WriteLine(string.Join("", statementTokens.Select(t => t.Str)));
statementTokens.Clear();
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output

#RufusL shows you how to write a method with the same postcondition, but doesn't really discuss any other characteristics of the algorithm. In particular, his algorithm has higher complexity than the C++ splice.
Namely, splice on a doubly linked list such as C++'s std::list is an O(1) operation, because it only requires a constant number of pointer swaps.
.NET does have a doubly linked list class in the base library, which is System.Collections.Generic.LinkedList, but it keeps pointers back to the list from each node (System.Collections.Generic.LinkedListNode, List property) and each list stores a count. As a result, in addition to the constant number of swaps of the forward and back pointers, O(n) node-to-list pointer updates will be required, and O(n) "distance" calculation is required in order to update the Count field on both lists.
So in order to achieve a true equivalent (O(1)) to C++ std::list::splice, one has to abandon the BCL LinkedList class and make a custom doubly linked list, with neither a cached Count field (LINQ Count() that walks the list will still work) nor pointers from list nodes to the list.

Related

How to use IF-ELSE in RPN(Reverse Polish Notation)?

i have done a RPN class to calculate strings which end-user input like
"1.0+3/2-tan(45)/(1+1)+sin(30)*abs(-1)+Abs(-10)"
Then, I want to parsing conditional statements and multi-parameters function such as "if(1>2,3/3,2*1)","max(1,2,3,4)"
So, my questions how to use IF-ELSE in the RPN?
Here's my code: enter link description here
For if(1>2,3/3,2*1) you would first evaluate the three argument from right to left and push their resuls on the stack so that it looked like this:
top-of-stack->false
1
2
Then if would be implemented in the RPN engine something like (pseudo-code):
void DoIf()
{
if (pop()) // pop result of "if" evaluation
{
var result = pop(); // pop "true" result from stack
pop(); // discard "false" result
push(result); // push back "true" result
}
else
{
pop(); // discard "true" result, leaving "false" result on stack
}
}
As for multi-parameter functions, there should be no special handling needed. Just evaluate and push all arguments (right to left, typically). The implementation of the function should pop off the required number of arguments and then push its result (if any).
i try to parse multi-parameters function such as if\Max before RPN.Parse()
public class MultiParameterFunctionParser
{
public readonly List<string> Funcs = new List<string> {"IF", "MAX"};
public string Parse(string exp)
{
while (IsFunction(exp,out var index,out var funcName))//
{
var parameters = GetParameters(exp, index, funcName, out var before, out var after);
var list = GetParameterList(parameters);
var value = Evaluate(list, funcName);
exp= $"{before}({value}){after}";
}
return exp;
}
/// <summary>
/// Is Exp Contains a function?
/// </summary>
/// <param name="exp"></param>
/// <param name="index"></param>
/// <param name="funcName"></param>
/// <returns></returns>
private bool IsFunction(string exp, out int index, out string funcName)
{
index = -1;
funcName = "";
foreach (var func in Funcs)
{
var idx = exp.IndexOf($"{func}(", StringComparison.CurrentCultureIgnoreCase);
if (idx == -1 || idx + 3 >= exp.Length - 1)
continue;
index = idx;
funcName = func;
break;
}
return index != -1 && index + 3 < exp.Length - 1;
}
/// <summary>
/// Get Parameters' string
/// </summary>
/// <param name="exp">8+if(12,sin(90),0)+1.2</param>
/// <param name="index">2 if's start index</param>
/// <param name="before">8+</param>
/// <param name="after">+1.2</param>
/// <returns>12,sin(90),0</returns>
private static string GetParameters(string exp,int index, string funcName, out string before, out string after)
{
before = exp.Substring(0, index);
index += funcName.Length + 1;
var leftCount = 1; // '(' count
var rightCount = 0;// ')' count
var results = "";
while (index < exp.Length && leftCount != rightCount)
{
var c = exp[index];
if (c.Equals('('))
leftCount++;
else if (c.Equals(')'))
rightCount++;
if (leftCount > rightCount)
results += c;
else
break;
index++;
}
after = exp.Substring(index + 1, exp.Length - index - 1);
return results;
}
/// <summary>
/// Parse Parameter string to list.
/// </summary>
/// <param name="exp">MAX(1,-1),1,0</param>
/// <returns>{"MAX(1,-1)","1","0"}</returns>
private static List<string> GetParameterList(string exp)
{
var count = exp.Length;
for (var i = count - 1; i > -1 && exp.Length > 0; i--)
{
var c = exp[i];
if (c != ',')
continue;
var after = exp.Substring(i + 1);
var before = exp.Substring(0,i);
if (after.Count(a => a == '(').Equals(after.Count(a => a == ')')))
{
exp = before + '#' + after;
}
}
var results = exp.Split('#').ToList();
return results;
}
private static double Evaluate(List<string> parameters, string funcName)
{
if (funcName.Equals("MAX", StringComparison.CurrentCultureIgnoreCase))
return EvaluateMax(parameters);
if (funcName.Equals("IF", StringComparison.CurrentCultureIgnoreCase))
return EvaluateIF(parameters);
return 0;
}
private static double EvaluateIF(List<string> parameters)
{
if (parameters == null || parameters.Count != 3)
throw new Exception("EvaluateIF parameters.Count()!=3");
var results = new List<double>();
foreach (var parameter in parameters)
{
var rpn = new RPN();
rpn.Parse(parameter);
var obj = rpn.Evaluate();
if (obj == null)
{
throw new Exception("EvaluateIF Not Number!");
}
if (obj.ToString().Equals("true", StringComparison.CurrentCultureIgnoreCase))
{
results.Add(1);
}
else if (obj.ToString().Equals("false", StringComparison.CurrentCultureIgnoreCase))
{
results.Add(-1);
}
else
{
if (double.TryParse(obj.ToString(), out var d))
results.Add(d);
else
throw new Exception("EvaluateIF Not Number!");
}
}
return results[0] >= 0 ? results[1] : results[2];
}
private static double EvaluateMax(IEnumerable<string> parameters)
{
var results = new List<double>();
foreach (var parameter in parameters)
{
var rpn = new RPN();
rpn.Parse(parameter);
var obj = rpn.Evaluate();
if (double.TryParse(obj.ToString(), out var d))
results.Add(d);
}
return results.Count > 0 ? results.Max() : 0;
}
}

HttpStatusCode to readable string

HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
HttpStatusCode statusCode = response.StatusCode;
In this code statusCode.ToString() returns for example "BadRequest" but I need "Bad Request"
I saw arcticles about response.ReasonPhrase, but that's not what I need and it is not supported by HttpWebResponse, only supported by HttpResponseMessage from HttpClient
Another example against Regex.Replace solution:
(414) RequestUriTooLong -> Request-Uri Too Long
Based on reference source, you can retrieve the English status description with a simple call into a static class, given the status code:
int code = 400;
/* will assign "Bad Request" to text */
var text = System.Web.HttpWorkerRequest.GetStatusDescription(code);
Texts are defined for ranges 100 - 507, returning empty strings for special codes like 418 and 506.
HttpStatusCode is an enum which has camel-cased member names.
You can use this one-liner to accomplish your need by putting space between camel-cased:
return Regex.Replace(statusCode.ToString(), "(?<=[a-z])([A-Z])", " $1", RegexOptions.Compiled);
Here, I pulled this out of a string utility class I have. Might be overkill, but it's a useful thing. Use the ToTitleCase extension method.
/// <summary>
/// A dictionary that holds the collection of previous title case
/// conversions so they don't have to be done again if needed more than once.
/// </summary>
private static Dictionary<string, string> _prevTitleCaseConversions = new Dictionary<String, String>();
/// <summary>
/// A collection of English words that should be lower-case in title-cased phrases.
/// </summary>
private static List<string> _englishTitleCaseLowerCaseWords = new List<string>() {"aboard", "about", "above", "across", "after",
"against", "along", "amid", "among", "anti", "around", "is", "as", "at", "before", "behind", "below",
"beneath", "beside", "besides", "between", "beyond", "but", "by", "concerning", "considering",
"despite", "down", "during", "except", "excepting", "excluding", "following", "for", "from", "in",
"inside", "into", "like", "minus", "near", "of", "off", "on", "onto", "opposite", "outside", "over",
"past", "per", "plus", "regarding", "round", "save", "since", "than", "through", "to", "toward",
"towards", "under", "underneath", "unlike", "until", "up", "upon", "versus", "via", "with", "within", "without",
"and", "but", "or", "nor", "for", "yet", "so", "although", "because", "since", "unless", "the", "a", "an"};
/// <summary>
/// Convert the provided alpha-numeric string to title case. The string may contain spaces in addition to letters and numbers, or it can be
/// one individual lowercase, uppercase, or camel case token.
/// </summary>
/// <param name="forValue">The input string which will be converted. The string can be a
/// normal string with spaces or a single token in all lowercase, all uppercase, or camel case.</param>
/// <returns>A version of the input string which has had spaces inserted between each internal "word" that is
/// delimited by an uppercase character and which has otherwise been converted to title case, i.e. all
/// words except for conjunctions, prepositions, and articles are upper case.</returns>
public static string ToTitleCase(this string forValue)
{
if (string.IsNullOrEmpty(forValue)) return forValue;
if (!Regex.IsMatch(forValue, "^[A-Za-z0-9 ]+$"))
throw new ArgumentException($#"""{forValue}"" is not a valid alpha-numeric token for this method.");
if (_prevTitleCaseConversions.ContainsKey(forValue)) return _prevTitleCaseConversions[forValue];
var tokenizedChars = GetTokenizedCharacterArray(forValue);
StringBuilder wordsSB = GetTitleCasedTokens(tokenizedChars);
string ret = wordsSB.ToString();
_prevTitleCaseConversions.Add(forValue, ret);
return ret;
}
/// <summary>
/// Convert the provided string such that first character is
/// uppercase and the remaining characters are lowercase.
/// </summary>
/// <param name="forInput">The string which will have
/// its first character converted to uppercase and
/// subsequent characters converted to lowercase.</param>
/// <returns>The provided string with its first character
/// converted to uppercase and subsequent characters converted to lowercase.</returns>
private static string FirstUpper(this string forInput)
{
return Alter(forInput, new Func<string, string>((input) => input.ToUpperInvariant()), new Func<string, string>((input) => input.ToLowerInvariant()));
}
/// <summary>
/// Return an array of characters built from the provided string with
/// spaces in between each word (token).
/// </summary>
private static ReadOnlyCollection<char> GetTokenizedCharacterArray(string fromInput)
{
var ret = new List<char>();
var tokenChars = fromInput.ToCharArray();
bool isPrevCharUpper = false;
bool isPrevPrevCharUpper = false;
bool isPrevPrevPrevCharUpper = false;
bool isNextCharUpper = false;
bool isNextCharSpace = false;
for (int i = 0; i < tokenChars.Length; i++)
{
char letter = tokenChars[i];
bool addSpace;
bool isCharUpper = char.IsUpper(letter);
if (i == 0) addSpace = false; // no space before first char.
else
{
bool isAtLastChar = i == tokenChars.Length - 1;
isNextCharUpper = !isAtLastChar && char.IsUpper(tokenChars[i + 1]);
isNextCharSpace = !isAtLastChar && !isNextCharUpper && tokenChars[i + 1].Equals(' ');
bool isInAcronym = (isCharUpper && isPrevCharUpper && (isAtLastChar || isNextCharSpace || isNextCharUpper));
addSpace = isCharUpper && !isInAcronym;
}
if (addSpace) ret.Add(' ');
ret.Add(letter);
isPrevPrevPrevCharUpper = isPrevPrevCharUpper;
isPrevPrevCharUpper = isPrevCharUpper;
isPrevCharUpper = isCharUpper;
}
return ret.AsReadOnly();
}
/// <summary>
/// Return a string builder that will produce a string which contains
/// all the tokens (words separated by spaces) in the provided collection
/// of characters and where the string conforms to title casing rules as defined above.
/// </summary>
private static StringBuilder GetTitleCasedTokens(IEnumerable<char> fromTokenChars)
{
StringBuilder wordsSB = new StringBuilder();
var comparer = StringComparer.Create(System.Globalization.CultureInfo.CurrentCulture, true);
var words = new string(fromTokenChars.ToArray()).Split(' ');
bool isFirstWord = true;
foreach (string word in words)
{
if (word.Length == 0) continue;
if (wordsSB.Length > 0) wordsSB.Append(' ');
bool isAcronym = word.Length > 1 && word.All((c) => char.IsUpper(c));
string toAppend;
// leave acronyms as-is, and lower-case all title case exceptions unless it's the first word.
if (isAcronym) toAppend = word;
else if (isFirstWord || !_englishTitleCaseLowerCaseWords.Contains(word, comparer)) toAppend = word.FirstUpper();
else toAppend = word.ToLower();
wordsSB.Append(toAppend);
isFirstWord = false;
}
return wordsSB;
}
/// <summary>
/// Convert the provided string such that first character is altered using
/// <paramref name="firstCharAlterationFunction"/> and the remaining characters
/// are altered using <paramref name="remainingCharsAlterationFunction"/>.
/// </summary>
/// <param name="forInput">The string which will have
/// its first character altered using <paramref name="firstCharAlterationFunction"/> and
/// subsequent characters altered using <paramref name="remainingCharsAlterationFunction"/>.</param>
/// <param name="firstCharAlterationFunction">The function which will
/// be used to alter the first character of the input string.</param>
/// <param name="remainingCharsAlterationFunction">The function which
/// will be used to ever character in the string after the first character.</param>
/// <returns>The provided string with its first character
/// altered using <paramref name="firstCharAlterationFunction"/> and
/// subsequent characters altered using <paramref name="remainingCharsAlterationFunction"/>.</returns>
private static string Alter(string forInput, Func<string, string> firstCharAlterationFunction, Func<string, string> remainingCharsAlterationFunction)
{
if (string.IsNullOrWhiteSpace(forInput)) return forInput;
if (forInput.Length == 1) return firstCharAlterationFunction(forInput);
return firstCharAlterationFunction(forInput[0].ToString()) + remainingCharsAlterationFunction(forInput.Substring(1));
}

How do I use First() in LINQ but random?

In a list like this:
var colors = new List<string>{"green", "red", "blue", "black","purple"};
I can get the first value like this:
var color = colors.First(c => c.StartsWidth("b")); //This will return the string with "blue"
Bot how do I do it, if I want want a random value matching the conditions? For example something like this:
Debug.log(colors.RandomFirst(c => c.StartsWidth("b"))) // Prints out black
Debug.log(colors.RandomFirst(c => c.StartsWidth("b"))) // Prints out black
Debug.log(colors.RandomFirst(c => c.StartsWidth("b"))) // Prints out blue
Debug.log(colors.RandomFirst(c => c.StartsWidth("b"))) // Prints out black
As in if there are multiple entries in the list matching the condition, i want to pull one of them randomly.
It has (I need it to be) to be an inline solution.
Thank you.
Random ordering then:
var rnd = new Random();
var color = colors.Where(c => c.StartsWith("b"))
.OrderBy(x => rnd.Next())
.First();
The above generates a random number for each element and sorts the results by that number.
You propbably won't notice a random results if you have only 2 elements matching your condition. But you can try the below sample (using the extension method below):
var colors = Enumerable.Range(0, 100).Select(i => "b" + i);
var rnd = new Random();
for (int i = 0; i < 5; i++)
{
Console.WriteLine(colors.RandomFirst(x => x.StartsWith("b"), rnd));
}
Output:
b23
b73
b27
b11
b8
You can create an extension method out of this called RandomFirst:
public static class MyExtensions
{
public static T RandomFirst<T>(this IEnumerable<T> source, Func<T, bool> predicate,
Random rnd)
{
return source.Where(predicate).OrderBy(i => rnd.Next()).First();
}
}
Usage:
var rnd = new Random();
var color1 = colors.RandomFirst(x => x.StartsWith("b"), rnd);
var color2 = colors.RandomFirst(x => x.StartsWith("b"), rnd);
var color3 = colors.RandomFirst(x => x.StartsWith("b"), rnd);
Optimization:
If you're worried about performance, you can try this optimized method (cuts the time to half for large lists):
public static T RandomFirstOptimized<T>(this IEnumerable<T> source,
Func<T, bool> predicate, Random rnd)
{
var matching = source.Where(predicate);
int matchCount = matching.Count();
if (matchCount == 0)
matching.First(); // force the exception;
return matching.ElementAt(rnd.Next(0, matchCount));
}
In case you have IList<T> you could also write a tiny extension method to pick a random element:
static class IListExtensions
{
private static Random _rnd = new Random();
public static void PickRandom<T>(this IList<T> items) =>
return items[_rnd.Next(items.Count)];
}
and use it like this:
var color = colors.Where(c => c.StartsWith("b")).ToList().PickRandom();
Another implementation is to extract all possible colors (sample) and take random one from them:
// Simplest, but not thread safe
private static Random random = new Random();
...
// All possible colors: [blue, black]
var sample = colors
.Where(c => c.StartsWidth("b"))
.ToArray();
var color = sample[random.Next(sample.Length)];
Simple way for short sequences if you don't mind iterating the sequence twice:
var randomItem = sequence.Skip(rng.Next(sequence.Count())).First();
For example (error handling elided for clarity):
var colors = new List<string> { "bronze", "green", "red", "blue", "black", "purple", "brown" };
var rng = new Random();
for (int i = 0; i < 10; ++i)
{
var sequence = colors.Where(c => c.StartsWith("b"));
var randomItem = sequence.Skip(rng.Next(sequence.Count())).First();
Console.WriteLine(randomItem);
}
This is an O(N) solution, but requires that the sequence is iterated once to get the count, then again to select a random item.
More complex solution using Reservoir Sampling suitable for long sequences
You can randomly select N items from a sequence of an unknown length in a single pass (O(N)) without resorting to expensive sorting, using a method known as Reservoir Sampling.
You would especially want to use Reservoir Sampling when:
The number of items to randomly choose from is large
The number of items to randomly choose from is unknown in advance
The number of items to randomly choose is small compared to the number of items to choose from
although you can use it for other situations too.
Here's a sample implementation:
/// <summary>
/// This uses Reservoir Sampling to select <paramref name="n"/> items from a sequence of items of unknown length.
/// The sequence must contain at least <paramref name="n"/> items.
/// </summary>
/// <typeparam name="T">The type of items in the sequence from which to randomly choose.</typeparam>
/// <param name="items">The sequence of items from which to randomly choose.</param>
/// <param name="n">The number of items to randomly choose<paramref name="items"/>.</param>
/// <param name="rng">A random number generator.</param>
/// <returns>The randomly chosen items.</returns>
public static T[] RandomlySelectedItems<T>(IEnumerable<T> items, int n, System.Random rng)
{
var result = new T[n];
int index = 0;
int count = 0;
foreach (var item in items)
{
if (index < n)
{
result[count++] = item;
}
else
{
int r = rng.Next(0, index + 1);
if (r < n)
result[r] = item;
}
++index;
}
if (index < n)
throw new ArgumentException("Input sequence too short");
return result;
}
For your case, you will need to pass n as 1, and you will receive an array of size 1.
You could use it like this (but note that this has no error checking, in the case that colors.Where(c => c.StartsWith("b") returns an empty sequence):
var colors = new List<string> { "green", "red", "blue", "black", "purple" };
var rng = new Random();
for (int i = 0; i < 10; ++i)
Console.WriteLine(RandomlySelectedItems(colors.Where(c => c.StartsWith("b")), 1, rng)[0]);
However, if you want to call this multiple times rather than just once, then you would be better off shuffling the array and accessing the first N items in the shuffled array. (It's hard to tell what your actual usage pattern will be from the question.)
I have created these two RandomOrDefault that are optimized to work on IList. One with predicate and one without it.
/// <summary>
/// Get a random element in the list
/// </summary>
public static TSource RandomOrDefault<TSource>(this IList<TSource> source)
{
if (source == null || source.Count == 0)
return default;
if (source.Count == 1)
return source[0];
var rand = new Random();
return source[rand.Next(source.Count)];
}
/// <summary>
/// Get a random element in the list that satisfies a condition
/// </summary>
public static TSource RandomOrDefault<TSource>(this IList<TSource> source, Func<TSource, bool> predicate)
{
if (source == null || source.Count == 0)
return default;
if (source.Count == 1)
{
var first = source[0];
if (predicate(first))
return first;
return default;
}
var matching = source.Where(predicate);
int matchCount = matching.Count();
if (matchCount == 0)
return default;
var rand = new Random();
return matching.ElementAt(rand.Next(matchCount));
}

List<string>.Contains using trim

It would be nice if this worked, but alas it doesn't.
List<string> items = new List<string>();
items.Add("a ");
bool useTrim = true;
if (items.Contains("a", useTrim)) {
Console.WriteLine("I'm happy");
}
I ended up implementing it as an extension method below. But I was wondering if anyone else had any elegant ideas other than creating a comparer class or looping through.
/// <summary>
/// Determines whether an element in the List of strings
/// matches the item. .Trim() is applied to each element
/// for the comparison
/// </summary>
/// <param name="value">a list of strings</param>
/// <param name="item">the string to search for in the list</param>
/// <returns>true if item is found in the list</returns>
public static bool ContainsTrimmed(this List<string> value, string item) {
bool ret = false;
if ((value.FindIndex(s => s.Trim() == item)) >= 0) {
ret = true;
}
return ret;
}
Well you'll either need to loop through it each time, or create another list of just the trimmed values, and use that for searching. (Heck, you could create a HashSet<string> if you only need to know whether or not a trimmed value is present.)
However, if you want to stick to just a single list, then rather than using FindIndex I'd use Any from LINQ:
if (items.Any(x => x.Trim() == item))
Note that even if you do want to keep your ContainsTrimmed method, you can simplify it to just:
return value.FindIndex(s => s.Trim() == item) >= 0;
I would suggest creating a custom IEqualityComparer to supply to the overloaded function Contains.
This is exactly the reason why this overload exists.
class TrimmedEqualityComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (x == null && y != null || x != null && y == null)
return false;
if (x == null && y == null)
return true;
return x.Trim() == y.Trim();
}
public int GetHashCode(string obj)
{
return obj != null ? obj.GetHashCode() : 0;
}
}
You call it like this.
var strs = new string[] {"a ", "b ", "c"};
if (strs.Contains("b", new TrimmedEqualityComparer()))
Console.WriteLine("I'm happy");

Linq - lookahead Iteration

I am iterating thru a collection using a visitor-type pattern and need to access the current and next item in the list. At the moment I am doing it via an extension method like this
public void Visit<TItem>(this IEnumerable<TItem> theList, Action<TItem, TItem> visitor)
{
for (i = 0; i <= theList.Count - 1; i++) {
if (i == theList.Count - 1) {
visitor(theList(i), null);
} else {
visitor(theList(i), theList(i + 1));
}
}
}
I was wondering whether there are other/better/more elegant ways to achieve this? At the moment I think I only need to have access to the current and next items in the list, but I'm wondering whether I may encounter situations where I may need to lookahead the next 'n' items, for example.
Assuming you're using .NET 4, you can use Zip to accomplish the same thing:
var query = original.Zip(original.Skip(1),
(current, next) => new { current, next });
This will iterate over the sequence twice though. A nicer alternative to your current extension method (which I don't believe will work, btw, as IEnumerable doesn't have a Count property, and you're trying to call theList as a method as well...) would be something like:
public static void Visit<TItem>(this IEnumerable<TItem> theList,
Action<TItem, TItem> visitor)
{
TItem prev = default(TItem);
using (var iterator = theList.GetEnumerator())
{
if (!iterator.MoveNext())
{
return;
}
prev = iterator.Current;
while (iterator.MoveNext())
{
TItem current = iterator.Current;
visitor(prev, current);
prev = current;
}
}
visitor(prev, default(TItem)); // Are you sure you want this?
}
A more general lookahead is trickier, to be honest... you'd want some sort of circular buffer, I suspect... probably a custom collection.
When we run into a similar task we have defined an extension methods:
/// <summary>
/// Projects a window of source elements in a source sequence into target sequence.
/// Thus
/// target[i] =
/// selector(source[i], source[i - 1], ... source[i - window + 1])
/// </summary>
/// <typeparam name="T">A type of elements of source sequence.</typeparam>
/// <typeparam name="R">A type of elements of target sequence.</typeparam>
/// <param name="source">A source sequence.</param>
/// <param name="window">A size of window.</param>
/// <param name="lookbehind">
/// Indicate whether to produce target if the number of source elements
/// preceeding the current is less than the window size.
/// </param>
/// <param name="lookahead">
/// Indicate whether to produce target if the number of source elements
/// following current is less than the window size.
/// </param>
/// <param name="selector">
/// A selector that derives target element.
/// On input it receives:
/// an array of source elements stored in round-robing fashon;
/// an index of the first element;
/// a number of elements in the array to count.
/// </param>
/// <returns>Returns a sequence of target elements.</returns>
public static IEnumerable<R> Window<T, R>(
this IEnumerable<T> source,
int window,
bool lookbehind,
bool lookahead,
Func<T[], int, int, R> selector)
{
var buffer = new T[window];
var index = 0;
var count = 0;
foreach(var value in source)
{
if (count < window)
{
buffer[count++] = value;
if (lookbehind || (count == window))
{
yield return selector(buffer, 0, count);
}
}
else
{
buffer[index] = value;
index = index + 1 == window ? 0 : index + 1;
yield return selector(buffer, index, count);
}
}
if (lookahead)
{
while(--count > 0)
{
index = index + 1 == window ? 0 : index + 1;
yield return selector(buffer, index, count);
}
}
}
/// <summary>
/// Projects a window of source elements in a source sequence into a
/// sequence of window arrays.
/// </summary>
/// <typeparam name="T">A type of elements of source sequence.</typeparam>
/// <typeparam name="R">A type of elements of target sequence.</typeparam>
/// <param name="source">A source sequence.</param>
/// <param name="window">A size of window.</param>
/// <param name="lookbehind">
/// Indicate whether to produce target if the number of source elements
/// preceeding the current is less than the window size.
/// </param>
/// <param name="lookahead">
/// Indicate whether to produce target if the number of source elements
/// following current is less than the window size.
/// </param>
/// <returns>Returns a sequence of windows.</returns>
public static IEnumerable<T[]> Window<T>(
this IEnumerable<T> source,
int window,
bool lookbehind,
bool lookahead)
{
return source.Window(
window,
lookbehind,
lookahead,
(buffer, index, count) =>
{
var result = new T[count];
for(var i = 0; i < count; ++i)
{
result[i] = buffer[index];
index = index + 1 == buffer.Length ? 0 : index + 1;
}
return result;
});
}
These functions help to produce output elements from a window of input elements.
See also LINQ extensions.
It seems like you are using the wrong type. The act of indexing an sequence will iterate it until it reaches the specified index every single time. Why not use IList<T> or ReadOnlyCollection<T>?
Not tested, but I think this works? When the visit would exceed the bounds it loops to the front of the list.
public class FriendlyEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> _enum;
public FriendlyEnumerable(IEnumerable<T> enumerable)
{
_enum = enumerable;
}
public void VisitAll(Action<T, T> visitFunc)
{
VisitAll(visitFunc, 1);
}
public void VisitAll(Action<T, T> visitFunc, int lookahead)
{
int index = 0;
int length = _enum.Count();
_enum.ToList().ForEach(t =>
{
for (int i = 1; i <= lookahead; i++)
visitFunc(t, _enum.ElementAt((index + i) % length));
index++;
});
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return _enum.GetEnumerator();
}
#endregion
}
You could use it like:
List<string> results = new List<string>();
List<string> strings = new List<string>()
{ "a", "b", "c", "d", "a", "b", "c", "d" };
FriendlyEnumerable<string> fe = new FriendlyEnumerable<string>(strings);
Action<string, string> compareString =
new Action<string,string>((s1, s2) =>
{
if (s1 == s2)
results.Add(s1 + " == " + s2);
});
fe.VisitAll(compareString);
//no results
fe.VisitAll(compareString, 4);
//8 results
public static void VisitLookAhead<TItem>(
this IEnumerable<TItem> source,
Action<IEnumerable<TItem>> visitor,
int targetSize
)
{
if (targetSize <= 1)
{
throw new Exception("invalid targetSize for VisitLookAhead");
}
List<List<TItem>> collections = new List<List<TItem>>();
// after 6th iteration with targetSize 6
//1, 2, 3, 4, 5, 6 <-- foundlist
//2, 3, 4, 5, 6
//3, 4, 5, 6
//4, 5, 6
//5, 6
//6
foreach(TItem x in source)
{
collections.Add(new List<TItem>());
collections.ForEach(subList => subList.Add(x));
List<TItem> foundList = collections
.FirstOrDefault(subList => subList.Count == targetSize);
if (foundList != null)
{
collections.Remove(foundList);
visitor(foundList);
}
}
//generate extra lists at the end - when lookahead will be missing items.
foreach(int i in Enumerable.Range(1, targetSize)
{
collections.ForEach(subList => subList.Add(default(TItem)));
List<TItem> foundList = collections
.FirstOrDefault(subList => subList.Count == targetSize);
if (foundList != null)
{
collections.Remove(foundList);
visitor(foundList);
}
}
}

Categories