Custom sorting of a string array in C# - c#

I have a string array or arraylist that is passed to my program in C#. Here is some examples of what those strings contain:
"Spr 2009"
"Sum 2006"
"Fall 2010"
"Fall 2007"
I want to be able to sort this array by the year and then the season. Is there a way to write a sorting function to tell it to sort by the year then the season. I know it would be easier if they were separate but I can't help what is being given to me.

You need to write a method which will compare any two strings in the appropriate way, and then you can just convert that method into a Comparison<string> delegate to pass into Array.Sort:
public static int CompareStrings(string s1, string s2)
{
// TODO: Comparison logic :)
}
...
string[] strings = { ... };
Array.Sort(strings, CompareStrings);
You can do the same thing with a generic list, too:
List<string> strings = ...;
strings.Sort(CompareStrings);

You could split the strings by the space character, convert both parts to integers and then use LINQ:
string[] seasons = new[] { "Spr", "Sum", "Fall", "Winter" };
string[] args = new[] { "Spr 2009", "Sum 2006", "Fall 2010", "Fall 2007" };
var result = from arg in args
let parts = arg.Split(' ')
let year = int.Parse(parts[1])
let season = Array.IndexOf(seasons, parts[0])
orderby year ascending, season ascending
select new { year, season };

You could always separate them. Create name-value-value triplets and work with them like that. Use Left and Right string functions if the data is formatted consistently. Then you sort on the year part first, and then the season part. Although Jon's idea seems really good, this is one idea of what to put in that method.

I believe what you're looking for is the StringComparer class.

var strings = new string[] {"Spr 2009", "Sum 2006", "Fall 2010", "Fall 2007"};
var sorted = strings.OrderBy(s =>
{
var parts = s.Split(' ');
double result = double.Parse(parts[1]);
switch(parts[0])
{
case "Spr":
result += .25;
break;
case "Sum"
result += .5;
break;
case "Fall":
result += .75;
break;
}
return result;
});
I also considered Array.Sort, which might be a little faster, but you also mentioned that sometimes these are ArrayLists.

Related

C# Sort string array alphabetically taking care of putting strings which start with capital letter first. first

1) There is a Kata which states to order all string in a string array, then take the first word and add *** between each letter: https://www.codewars.com/kata/sort-and-star
2) For example:
(1) It is given:
bitcoin
take
over
the
world
maybe
who
knows
perhaps
(2) After ordering it:
bitcoin
knows
maybe
over
perhaps
take
the
who
world
(3) The return result is:
b***i***t***c***o***i***n
3) However the difficulty I am facing is the following: How we can express 'order first the words which start with capital letter'?
4) I have tried the following code:
using System;
public class Kata
{
public static string TwoSort(string[] s)
{
foreach(string str in s){
Console.WriteLine(str);
}
Console.WriteLine("");
Array.Sort(s);
foreach(string str in s){
Console.WriteLine(str);
}
Console.WriteLine("");
string firstWord = s[0];
string result = "";
foreach(char letter in firstWord){
result += letter + "***";
}
Console.WriteLine(result.Substring(0, result.Length - 3));
return result.Substring(0, result.Length - 3);
}
}
5) For example:
(1) It is given the following array:
Lets
all
go
on
holiday
somewhere
very
cold
(2) After ordering it:
all
cold
go
holiday
Lets
on
somewhere
very
(3) Current wrong result:
a***l***l
(4) Expected correct result:
L***e***t***s
I have also read:
how to sort a string array by alphabet?
Sorting an array alphabetically in C#
You should specify the comparer, e.g. (Linq solution):
string[] source = new string[] {
"Lets",
"all",
"go",
"on",
"holiday",
"somewhere",
"very",
"cold",
};
// StringComparer.Ordinal: order by Ascii values; capital letters < small letters
var ordered = source
.OrderBy(item => item, StringComparer.Ordinal);
Console.Write(string.Join(", ", ordered));
Outcome:
Lets, all, cold, go, holiday, on, somewhere, very
To obtain the desired outcome (in case you insist on ordering), you can put
var result = string.Join("***", source
.OrderBy(item => item, StringComparer.Ordinal)
.First()
.Select(c => c)); // <- turn string into IEnumerable<char>
Console.Write(result);
Outcome:
L***e***t***s
In case you want to keep on using your current code, change Array.Sort(s); into
Array.Sort(s, StringComparer.Ordinal);
You can specify the Ordinal string comparer to short the result by capital letter then lowercase letter.
Array.Sort(s, StringComparer.Ordinal);
If it is a kind of Class with Feature like "Name", You can use the following
if (isAscend)
List1.Sort((x, y) => x.Name.CompareTo(y.Name));
else
List1.Sort((x, y) => -x.Name.CompareTo(y.Name));
You can get a the Class List of sort by "Name".

Is there a way to select two parts of an array and use both at the same time?

Is there a way to select two parts of an array and use both at the same time? I am using this to mask some fields when a form is viewed. The first part for the SSN works, I just dont know how to do the second part. Any help is appreciated. :)
string SSN = fields.GetField("txt_SSN");
string[] strArray = SSN.Split('-');
if (strArray.Length <= 3)
{
fields.SetField("txt_SSN", string.Format("XXX-XX-{0}", strArray[2]));
}
string BDATE = fields.GetField("txt_Date_Of_Birth");
string[] strArr = BDATE.Split('/');
if (strArr.Length <= 3)
{
fields.SetField("txt_Date_Of_Birth", string.Format("{0}-{1}-XXXX", strArr[0]));
}
Sure, just use it with the other item you're looking for. Say you're trying to use the first and second item in the array the following should work:
var BDATE = "08/12/2014";
string[] strArr = BDATE.Split('/');
fields.SetField("txt_Date_Of_Birth",
string.Format("{0}-{1}-XXXX", strArr[0], strArr[1]));
Given a date like 08/12/2014, this will output 08-12-XXXX.

Parse multiple values from string c#

Suppose I have written "5 and 6" or "5+6". How can I assign 5 and 6 to two different variables in c# ?
P.S. I also want to do certain work if certain chars are found in string. Suppose I have written 5+5. Will this code do that ?
if(string.Contains("+"))
{
sum=x+y;
}
string input="5+5";
var numbers = Regex.Matches(input, #"\d+")
.Cast<Match>()
.Select(m => m.Value)
.ToList();
Personally, I would vote against doing some splitting and regular expression stuff.
Instead I would (and did in the past) use one of the many Expression Evaluation libraries, like e.g. this one over at Code Project (and the updated version over at CodePlex).
Using the parser/tool above, you could do things like:
A simple expression evaluation then could look like:
Expression e = new Expression("5 + 6");
Debug.Assert(11 == e.Evaluate());
To me this is much more error-proof than doing the parsing all by myself, including regular expressions and the like.
You should use another name for your string than string
var numbers = yourString.Split("+");
var sum = Convert.ToInt32(numbers[0]) + Convert.ToInt32(numbers[1]);
Note: Thats an implementation without any error checking or error handling...
If you want to assign numbers from string to variables, you will have to parse string and make conversion.
Simple example, if you have text with only one number
string text = "500";
int num = int.Parse(text);
Now, if you want to parse something more complicated, you can use split() and/or regex to get all numbers and operators between them. Than you just iterate array and assign numbers to variables.
string text = "500+400";
if (text.Contains("+"))
{
String[] data = text.Split("+");
int a = int.Parse(data[0]);
int b = int.Parse(data[1]);
int res = a + b;
}
Basicly, if you have just 2 numbers and operazor between them, its ok. If you want to make "calculator" you will need something more, like Binary Trees or Stack.
Use the String.Split method. It splits your string rom the given character and returns a string array containing the value that is broken down into multiple pieces depending on the character to break, in this case, its "+".
int x = 0;
int y = 0;
int z = 0;
string value = "5+6";
if (value.Contains("+"))
{
string[] returnedArray = value.Split('+');
x = Convert.ToInt32(returnedArray[0]);
y = Convert.ToInt32(returnedArray[1]);
z = x + y;
}
Something like this may helpful
string strMy = "5&6";
char[] arr = strMy.ToCharArray();
List<int> list = new List<int>();
foreach (char item in arr)
{
int value;
if (int.TryParse(item.ToString(), out value))
{
list.Add(item);
}
}
list will contains all the integer values
You can use String.Split method like;
string s = "5 and 6";
string[] a = s.Split(new string[] { "and", "+" }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine(a[0].Trim());
Console.WriteLine(a[1].Trim());
Here is a DEMO.
Use regex to get those value and then switch on the operand to do the calculation
string str = "51 + 6";
str = str.Replace(" ", "");
Regex regex = new Regex(#"(?<rightHand>\d+)(?<operand>\+|and)(?<leftHand>\d+)");
var match = regex.Match(str);
int rightHand = int.Parse(match.Groups["rightHand"].Value);
int leftHand = int.Parse(match.Groups["leftHand"].Value);
string op = match.Groups["operand"].Value;
switch (op)
{
case "+":
.
.
.
}
Split function maybe is comfortable in use but it is space inefficient
because it needs array of strings
Maybe Trim(), IndexOf(), Substring() can replace Split() function

substring with linq?

I've got collection of words, and i wanna create collection from this collection limited to 5 chars
Input:
Car
Collection
Limited
stackoverflow
Output:
car
colle
limit
stack
word.Substring(0,5) throws exception (length)
word.Take(10) is not good idea, too...
Any good ideas ??
LINQ to objects for this scenario? You can do a select as in this:
from w in words
select new
{
Word = (w.Length > 5) ? w.Substring(0, 5) : w
};
Essentially, ?: gets you around this issue.
var words = new [] { "Car", "Collection", "Limited", "stackoverflow" };
IEnumerable<string> cropped = words.Select(word =>
word[0..Math.Min(5, word.Length)]);
Ranges are available in C# 8, otherwise you'll need to do:
IEnumerable<string> cropped = words.Select(word =>
word.Substring(0, Math.Min(5, word.Length)));
Something you can do, is
string partialText = text.Substring(0, Math.Min(text.Length, 5));
I believe the kind of answer you were looking for would look like this:
var x = new string[] {"car", "Collection", "Limited", "stackoverflow" };
var output = x.Select(word => String.Join("", word.Take(5).ToList()));
The variable "output" contains the result:
car
Colle
Limit
stack
and the string "car" doesn't throw an exception.
But while Join and Take(5) works, it's generally much simpler to use, as was suggested in another answer,
subString = word.Substring(0,Math.Min(5,word.Length));
The latter code is more human-readable and lightweight, though there is definitely a slight coolness factor to using Linq on a string to take the first five characters, without having to check the length of the string.

Simple LINQ query

I have a collection like this
List<int> {1,15,17,8,3};
how to get a flat string like "1-15-17-8-3" through LINQ query?
thank you
something like...
string mystring = string.Join("-", yourlist.Select( o => o.toString()).toArray()));
(Edit: Now its tested, and works fine)
You can write an extension method and then call .ToString("-") on your IEnumerable object type as shown here:
int[] intArray = { 1, 2, 3 };
Console.WriteLine(intArray.ToString(","));
// output 1,2,3
List<string> list = new List<string>{"a","b","c"};
Console.WriteLine(intArray.ToString("|"));
// output a|b|c
Examples of extension method implementation are here:
http://coolthingoftheday.blogspot.com/2008/09/todelimitedstring-using-linq-and.html
http://www.codemeit.com/linq/c-array-delimited-tostring.html
Use Enumerable.Aggregate like so:
var intList = new[] {1,15,17,8,3};
string result = intList.Aggregate(string.Empty, (str, nextInt) => str + nextInt + "-");
This is the standard "LINQy" way of doing it - what you're wanting is the aggregate. You would use the same concept if you were coding in another language, say Python, where you would use reduce().
EDIT:
That will get you "1-15-17-8-3-". You can lop off the last character to get what you're describing, and you can do that inside of Aggregate(), if you'd like:
string result = intList.Aggregate(string.Empty, (str, nextInt) => str + nextInt + "-", str => str.Substring(0, str.Length - 1));
The first argument is the seed, the second is function that will perform the aggregation, and the third argument is your selector - it allows you to make a final change to the aggregated value - as an example, your aggregate could be a numeric value and you want return the value as a formatted string.
HTH,
-Charles
StringBuilder sb = new StringBuilder();
foreach(int i in collection)
{
sb.Append(i.ToString() + "-");
}
string result = sb.ToString().SubString(0,sb.ToString().ToCharArray().Length - 2);
Something like this perhaps (off the top of my head that is!).
The best answer is given by Tim J.
If, however, you wanted a pure LINQ solution then try something like this (much more typing, and much less readable than Tim J's answer):
string yourString = yourList.Aggregate
(
new StringBuilder(),
(sb, x) => sb.Append(x).Append("-"),
sb => (sb.Length > 0) ? sb.ToString(0, sb.Length - 1) : ""
);
(This is a variation on Charles's answer, but uses a StringBuilder rather than string concatenation.)

Categories