List<string> not sorting - c#

The string is returned the same as it enters the function:
public static string AlphaSort(string S)
{
var NewS = new List<string>();
var Arr = S.Replace(" ", "").Split();
NewS = Arr.ToList();
var SortedS = NewS.OrderBy(c => c).ToList();
return String.Join("", SortedS.ToArray());
}
I have also tried NewS.Sort() to no avail

I think you're overthinking things. Try something like:
public static string AlphaSort( string s )
{
string sorted = string.Join( "" ,
( s ?? "")
.Split()
.OrderBy( x => x )
) ;
return sorted ;
}
which turns this string
"The Quick Brown Fox Jumped Over The Lazy Dog"
into this string
"BrownDogFoxJumpedLazyOverQuickTheThe"
If you're trying to order the individual characters, it gets much easier. A string is an IEnumerable<char> (very convenient). It's also readily convertable to a char[] (since that's ultimately its backing store). In this case, something like
public static string AlphaSort( string s )
{
string sorted = new string(
(s ?? "")
.Where( c => !char.IsWhiteSpace(c) )
.OrderBy( c => c )
);
return sorted ;
}
Given the input noted above,
"The Quick Brown Fox Jumped Over The Lazy Dog"
this produces the string
"BDFJLOQTTacdeeeeghhikmnoooprruuvwxyz"
One might note that once you're looking at characters, the ordering is no longer really alphabetic. Rather, it's an ordinal sort, since a char is an unsigned short.
Here's another approach
public static string AlphaSort( string s )
{
char[] chars = s.ToCharArray() ;
Array.Sort( chars ) ;
int i = 0 ;
while ( i < chars.Length && char.IsWhiteSpace(chars[i]) ) ++i ;
string sorted = new string( chars , i , chars.Length-i ) ;
return sorted ;
}
producing the same
"BDFJLOQTTacdeeeeghhikmnoooprruuvwxyz"
from
"The Quick Brown Fox Jumped Over The Lazy Dog"
Cheers!

I think you're trying to take a string and return the characters alphabetically. If so, try this
public static string AlphaSort(string S)
{
var Arr = S.Replace(" ", "").ToCharArray();
var SortedS = Arr.OrderBy(c => c);
return String.Join("", SortedS);
}

public static string AlphaSort(string S)
{
var Arr = S.Replace(" ", "").Split(' ');
var NewS = Arr.ToList();
var SortedS = NewS.OrderBy(c => c).ToList();
return String.Join("", SortedS.ToArray());
}
or simply you could use without using Replace:
var Arr = S.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries)

This will return the string sorted case insensitive
public static string AlphaSort(string S)
{
string[] NewS = S.Split(' ');
Array.Sort(NewS, new CaseInsensitiveComparer());
return String.Join(" ", NewS);
}

Related

Retrieving Numeric value before a decimal in a string value

I am working on a routine in C#
I have a list of alphanumeric sheet numbers that I would like to retrieve the numbers before the decimal to use in my routine.
FP10.01-->10
M1.01-->1
PP8.01-->8
If possible, how can something like this be achieved as either a string or integer?
You could use a regex:
Regex r = new Regex("([0-9]+)[.]");
string s = "FP10.01";
var result = Convert.ToInt32(r.Match(s).Groups[1].ToString()); //10
string input = "FP10.01";
string[] _input = input.Split('.');
string num = find(_input[0]);
public string find(string input)
{
char[] _input = input.ToArray();
int number;
string result = null;
foreach (var item in _input)
{
if (int.TryParse(item.ToString(), out number) == true)
{
result = result + number;
}
}
return result;
}
To accumulate the resulting elements into a list, you can do something like:
List<string> myList = new List<string>(){ "FP10.01","M1.01", "PP8.01"};
List<int> resultSet =
myList.Select(e =>
Regex.Replace(e.Substring(0, e.IndexOf('.')), #"[^\d]", string.Empty))
.Select(int.Parse)
.ToList();
This will take each element in myList and in turn, take a substring of each element from index 0 until before the . and then replace all the non-numeric data with string.Empty and then finally parse the string element into an int and store it into a list.
another variant would be:
List<int> resultSet =
myList.Select(e => e.Substring(0, e.IndexOf('.')))
.Select(e => string.Join(string.Empty, e.Where(char.IsDigit)))
.Select(int.Parse)
.ToList();
or if you want the elements to be strings then you could do:
List<string> resultSet =
myList.Select(e => e.Substring(0, e.IndexOf('.')))
.Select(e => string.Join(string.Empty, e.Where(char.IsDigit)))
.ToList();
To retrieve a single element of type string then you can create a helper function as such:
public static string GetValueBeforeDot(string input){
return input.Substring(0, input.IndexOf('.'))
.Where(char.IsDigit)
.Aggregate(string.Empty, (e, a) => e + a);
}
To retrieve a single element of type int then the helper function should be:
public static int GetValueBeforeDot(string input){
return int.Parse(input.Substring(0, input.IndexOf('.'))
.Where(char.IsDigit)
.Aggregate(string.Empty, (e, a) => e + a));
}
This approach removes alphabet characters by replacing them with an empty string. Splitting on the '.' character will leave you with a two element array consisting of numbers at index 0 and after decimal values at index 1.
string input = "FP10.01";
var result = Regex.Replace(input, #"([A-Za-z]+)", string.Empty).Split('.');
var beforeDecimalNumbers = result[0]; // 10
var afterDecimalNumbers = result[1]; // 01

How to find 1 in my string but ignore -1 C#

I have a string
string test1 = "255\r\n\r\n0\r\n\r\n-1\r\n\r\n255\r\n\r\n1\r";
I want to find all the 1's in my string but not the -1's. So in my string there is only one 1. I use string.Contain("1") but this will find two 1's. So how do i do this?
You can use regular expression:
string test1 = "255\r\n\r\n0\r\n\r\n-1\r\n\r\n255\r\n\r\n1\r";
// if at least one "1", but not "-1"
if (Regex.IsMatch(test1, "(?<!-)1")) {
...
}
the pattern is exactly 1 which is not preceed by -. To find all the 1s:
var matches = Regex
.Matches(test1, "(?<!-)1")
.OfType<Match>()
.ToArray(); // if you want an array
Try this simple solution:
Note : You can convert this to extension Method Easily.
static List<int> FindIndexSpecial(string search, char find, char ignoreIfPreceededBy)
{
// Map each Character with its Index in the String
var characterIndexMapping = search.Select((x, y) => new { character = x, index = y }).ToList();
// Check the Indexes of the excluded Character
var excludeIndexes = characterIndexMapping.Where(x => x.character == ignoreIfPreceededBy).Select(x => x.index).ToList();
// Return only Indexes who match the 'find' and are not preceeded by the excluded character
return (from t in characterIndexMapping
where t.character == find && !excludeIndexes.Contains(t.index - 1)
select t.index).ToList();
}
Usage :
static void Main(string[] args)
{
string test1 = "255\r\n\r\n0\r\n\r\n-1\r\n\r\n255\r\n\r\n1\r";
var matches = FindIndexSpecial(test1, '1', '-');
foreach (int index in matches)
{
Console.WriteLine(index);
}
Console.ReadKey();
}
You could use String.Split and Enumerable.Contains or Enumerable.Where:
string[] lines = test1.Split(new[] {Environment.NewLine, "\r"}, StringSplitOptions.RemoveEmptyEntries);
bool contains1 = lines.Contains("1");
string[] allOnes = lines.Where(l => l == "1").ToArray();
String.Contains searches for sub-strings in a given string instance. Enumerable.Contains looks if there's at least one string in the string[] which equals it.

Get the difference between 2 strings

I'm attempting to calculate the difference between two strings
For example
string val1 = "Have a good day";
string val2 = "Have a very good day, Joe";
The result would be a list of string, with 2 items "very " and ", Joe"
So far my research into this task hasn't turned up much
Edit: The result would probably need to be 2 separate lists of strings, one that hold additions, and one that hold removals
This is the simplest version I can think of:
class Program
{
static void Main(string[] args)
{
string val1 = "Have a good day";
string val2 = "Have a very good day, Joe";
MatchCollection words1 = Regex.Matches(val1, #"\b(\w+)\b");
MatchCollection words2 = Regex.Matches(val2, #"\b(\w+)\b");
var hs1 = new HashSet<string>(words1.Cast<Match>().Select(m => m.Value));
var hs2 = new HashSet<string>(words2.Cast<Match>().Select(m => m.Value));
// Optionaly you can use a custom comparer for the words.
// var hs2 = new HashSet<string>(words2.Cast<Match>().Select(m => m.Value), new MyComparer());
// h2 contains after this operation only 'very' and 'Joe'
hs2.ExceptWith(hs1);
}
}
custom comparer:
public class MyComparer : IEqualityComparer<string>
{
public bool Equals(string one, string two)
{
return one.Equals(two, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(string item)
{
return item.GetHashCode();
}
}
Actually i followed this steps,
(i)Obtain all words from two words irrespective of special characters
(ii)From the two lists find the difference
CODE:
string s2 = "Have a very good day, Joe";
IEnumerable<string> diff;
MatchCollection matches = Regex.Matches(s1, #"\b[\w']*\b");
IEnumerable<string> first= from m in matches.Cast<Match>()
where !string.IsNullOrEmpty(m.Value)
select TrimSuffix(m.Value);
MatchCollection matches1 = Regex.Matches(s2, #"\b[\w']*\b");
IEnumerable<string> second = from m in matches1.Cast<Match>()
where !string.IsNullOrEmpty(m.Value)
select TrimSuffix(m.Value);
if (second.Count() > first.Count())
{
diff = second.Except(first).ToList();
}
else
{
diff = first.Except(second).ToList();
}
}
static string TrimSuffix(string word)
{
int apostropheLocation = word.IndexOf('\'');
if (apostropheLocation != -1)
{
word = word.Substring(0, apostropheLocation);
}
return word;
}
OUTPUT:
very, Joe
This code:
enum Where { None, First, Second, Both } // somewhere in your source file
//...
var val1 = "Have a good calm day calm calm calm";
var val2 = "Have a very good day, Joe Joe Joe Joe";
var words1 = from m in Regex.Matches(val1, "(\\w+)|(\\S+\\s+\\S+)").Cast<Match>()
where m.Success
select m.Value.ToLower();
var words2 = from m in Regex.Matches(val2, "(\\w+)|(\\S+\\s+\\S+)").Cast<Match>()
where m.Success
select m.Value.ToLower();
var dic = new Dictionary<string, Where>();
foreach (var s in words1)
{
dic[s] = Where.First;
}
foreach (var s in words2)
{
Where b;
if (!dic.TryGetValue(s, out b)) b = Where.None;
switch (b)
{
case Where.None:
dic[s] = Where.Second;
break;
case Where.First:
dic[s] = Where.Both;
break;
}
}
foreach (var kv in dic.Where(x => x.Value != Where.Both))
{
Console.WriteLine(kv.Key);
}
Gives us 'calm', 'very', ', Joe' and 'Joe' which are differences from both strings; 'calm' from the first one and 'very', ', Joe' and 'Joe' from the next one. It also removes repeated cases.
And to get two separate lists that shows us which word came from which text:
var list1 = dic.Where(x => x.Value == Where.First).ToList();
var list2 = dic.Where(x => x.Value == Where.Second).ToList();
foreach (var kv in list1)
{
Console.WriteLine("{0}: {1}", kv.Key, kv.Value);
}
foreach (var kv in list2)
{
Console.WriteLine("{0}: {1}", kv.Key, kv.Value);
}
Put the characters into two sets then compute the relative compliment of those sets.
The relative compliment will be available in any good set library.
You might want to take care to preserve the order of the characters.
you have to remove the ',' in order to get the expected result
string s1 = "Have a good day";
string s2 = "Have a very good day, Joe";
int index = s2.IndexOf(','); <----- get the index of the char to be removed
IEnumerable<string> diff;
IEnumerable<string> first = s1.Split(' ').Distinct();
IEnumerable<string> second = s2.Remove(index, 1).Split(' ').Distinct();<--- remove it
if (second.Count() > first.Count())
{
diff = second.Except(first).ToList();
}
else
{
diff = first.Except(second).ToList();
}

Remove Alphabetic characters from a string, leaving numbers and symbols

I have a bunch of strings that I'm trying to parse the date out of. I have a script that will parse the date, but it's having trouble with all the extra letters in the string. I need to remove all the letters but leave characters such as - / _
I'm not particularly good with Regex, so all attempts to do this so far have ended with too many characters getting removed.
Here's a few sample strings to help:
Littleton, CO - Go-Live 5/8
Brunswick - Go-Live 5/14
CutSheeet_Go Live-5-14-14
Go Live - 5-19-2014
You could do this:
Regex.Replace(input, "([a-zA-Z,_ ]+|(?<=[a-zA-Z ])[/-])", "");
Working regex example:
http://regex101.com/r/kD2jF4
From your example data, output would be:
5/8
5/14
5-14-14
5-19-2014
You can use a function like this:
public static string Parse(string source)
{
var numbers = new [] {'0','1','2','3','4','5','6','7','8','9' };
var chars = new [] { '-', '/', '_' };
return new string(source
.Where(x => numbers.Contains(x) || chars.Contains(x))
.ToArray()).Trim(chars);
}
Here is fiddle
Try this:
public static string StripCrap(string input)
{
return input.Where(c => char.IsNumber(c) || c == '_' || c == '/' ||
c == '-').Aggregate("", (current, c) => current + c);
}
Or, if you want a maintainable list:
public static string StripCrap(string input)
{
char[] nonCrapChars = {'/', '-', '_'};
return input.Where(c => char.IsNumber(c) || nonCrapChars.Contains(c)).Aggregate("", (current, c) => current + c);
}
Or...You could also create an extension method:
public static string ToNonCrappyString(this string input)
{
char[] nonCrapChars = {'/', '-', '_'};
return input.Where(c => char.IsNumber(c) || nonCrapChars.Contains(c)).Aggregate("", (current, c) => current + c);
}
and you can call it like this:
string myString = "Hello 1234!";
string nonCrappyString = myString.ToNonCrappyString();
use this pattern .*?(\d+[\d-\/]*\d+)|.* and replace with $1 Demo

Implementing method in a functional style

I have the following method:
private List<string> CreateSegments(string virtualPath)
{
List<string> segments = new List<string>();
int i = virtualPath.IndexOf('/', 1);
while (i >= 0 && i < virtualPath.Length)
{
var segment = virtualPath.Substring(0, i);
if (!string.IsNullOrWhiteSpace(segment))
{
segments.Add(segment);
segments.Add(VirtualPathUtility.Combine(segment, "default"));
}
i = virtualPath.IndexOf('/', i + 1);
}
segments.Add(virtualPath);
segments.Add(VirtualPathUtility.Combine(virtualPath, "default"));
return segments;
}
Basically, it creates path segments which I will use to check if a file exists in any of those segments. Like this:
string[] extensions = GetRegisteredExtensions();
HttpServerUtilityBase server = HttpContext.Current.Server;
List<string> segments = CreateSegments(virtualPath);
// check if a file exists with any of the registered extensions
var match = extensions.SelectMany(x => segments.Select(s => string.Format("{0}.{1}", s, x)))
.FirstOrDefault(p => System.IO.File.Exists(server.MapPath(p)));
All the above code looks like it could use some clean up and optimization, but I'm looking for a way to use LINQ if possible to generate the segments.
Something like: var segments = virtualPath.Split('/').SelectMany(...) and get a result similar to the following:
/path
/path/default
/path/to
/path/to/default
/path/to/file
/path/to/file/default
Where virtualPath would contain the value "/path/to/file"
EDIT: Changed string.Format("{0}/{1}", ...) to VirtualPathUtility.Combine(..., ...)
Any ideas?
One way would be to incrementally select the path segments, then "join" it with an empty string and "/default" to get the two variations:
string path = #"/path/to/file";
string temp = "";
var query = path.Split('/')
.Where(s => !string.IsNullOrEmpty(s))
.Select((p) => {temp += ("/" + p); return temp;} )
.SelectMany(s => new[]{"","/default"}.Select (d => s + d) );
If you first define an extension method like this:
public static IEnumerable<int> SplitIndexes(this string subject, char search)
{
for(var i = 1; i < subject.Length; i++)
{
if(subject[i] == search)
{
yield return i;
}
}
yield return subject.Length;
}
Then you could do this:
var endings = new string[] { string.Empty, "/default" };
var virtualPath = "/path/to/file";
var results =
from i in virtualPath.SplitIndexes('/')
from e in endings
select virtualPath.Substring(0, i) + e;
Or if you prefer query syntax:
var endings = new string[] { string.Empty, "/default" };
var virtualPath = "/path/to/file";
var results = virtualPath.SplitIndexes('/')
.SelectMany(i => endings.Select(e => virtualPath.Substring(0, i) + e));
The result will be:
/path
/path/default
/path/to
/path/to/default
/path/to/file
/path/to/file/default
As others have suggested, you can this in a more platform independent way by using Path.Combine, like this:
var endings = new string[] { string.Empty, "default" }; // Note: no / before default
var results =
from i in virtualPath.SplitIndexes(Path.DirectorySeparatorChar)
from e in endings
select Path.Combine(virtualPath.Substring(0, i), e);
This might do the trick. It is not the most succinct code but it seems very readable to me. It uses string concatenation because for short strings, like paths or URLs, it is faster than any of the alternatives.
Edit: fixed and tested.
var query = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(new List<string>(), (memo, segment) => {
memo.Add(memo.DefaultIfEmpty("").Last() + "/" + segment);
return memo;
}).Aggregate(new List<string>(), (memo, p) => {
memo.Add(p);
memo.Add(p + "/default");
return memo;
});
The higher-order function you're looking for is called scan. There is no such function in normal LINQ, but you can find it in MoreLinq. Using that, your code could look like this:
private List<string> CreateSegments(string virtualPath)
{
return virtualPath.Split('/')
.Scan((s1, s2) => s1 + '/' + s2)
.Skip(1)
.SelectMany(p => new[] { p, p + "/default" })
.ToList();
}
This assumes your path will be always an absolute path starting with a /. For relative paths, you will need to remove the .Skip(1) part.
If you don't want to get MoreLinq just for this one method, you can just copy its source into your project.
The provided answers so far are more succinct, but this is what I came up with:
public static IEnumerable<string> PossiblePaths(string basePath)
{
return PossiblePaths(basePath.Split(new[] { "/" },
StringSplitOptions.RemoveEmptyEntries));
}
private static IEnumerable<string> PossiblePaths(IEnumerable<string> segments,
string current = "/")
{
if (segments.Count() == 0)
{
return new string[0];
}
else
{
string next = current + segments.First();
return new[] { next, next + "/default" }
.Concat(PossiblePaths(segments.Skip(1), next + "/"));
}
}
Something like this:
public static IEnumerable<string> EnumerateSegments( this IEnumerable<string> segments )
{
StringBuilder sb = new StringBuilder() ;
foreach ( string segment in segements )
{
sb.Append( Path.DirectorySeparatorChar ).Append( segment ) ;
yield return sb.ToString() ;
int n = sb.Length ;
sb.Append( Path.DirectorySeparatorChar ).Append("default") ;
yield return sb.ToString() ;
sb.Length = n ;
}
}
ought to do you.

Categories

Resources