Sorting a List alphabetically and numerically - c#

I've written a code that has a list and I want to sort the list alphabetically and numerically.
For example the first items in the list are
list[0] = "INPUT 10"
list[1] = "INPUT 5".
I want my list reorganized like this:
list[0] = "INPUT 5"
list[1] = "INPUT 10".
So basically my program gets checked items from a checked list box,stores them in a list, and I want it to reorganize the list alphabettically.
The checked list box has items like INPUT 1,INPUT 2,INPUT 3...and so fourth. Can anyone suggest me a way of how to go about this?
UPDATED CODE
I've updated my code and now this code splits the strings into INPUT and 10.The "q" list obtains the checked items in the input box,the string "s" array gets the splittd data from the q list. Then the "numbers" list gets only the number part of the string for example "INPUT 5",the number list will only get "5".Then I want to sort these numbers and build another string combining the sorted number list and the string "INPUT" and add it to the output checkedlistbox. My code isnt working though...any suggestions?It should sort the numbers but it doesnt...anyone have any suggestions of why this code isnt working? And I keep on getting error messages of the array being able to unhandle negative integers and what not.
List<string> q = new List<string>();
List<string> numbers = new List<string>();
private void button_ekle_Click(object sender, EventArgs e)
{
for (int k = clb_input.Items.Count - 1; k >= 0; k--)
{
if (clb_input.GetItemChecked(k) == true)
{
q.Add(clb_input.Items[k].ToString());
//clb_output.Items.Add(clb_input.Items[k]);
clb_input.Items.RemoveAt(k);
}
else { }
}
string[] s = new string[q.Count * 2];
//string[] numbers=new string[q.Count/2];
for (int t = 1; t <= q.Count * 2; t++)
{
if (q != null)
s = q[t - 1].ToString().Split(' ');
else { s[t] = null; }
}
for (int x = 1; x <= q.Count; x++)
{
if (s[2 * x - 1] != null)
{
numbers[x - 1] = s[2 * x - 1];
numbers.Sort();
clb_output.Items.Add("INPUT "+ numbers[x - 1].ToString());
}
else { numbers[x - 1] = null; }
}
}

What you need is Alphanumeric Sorting ( most commonly seen in windows explorer, the way files are sorted)
Code can be found here : http://www.dotnetperls.com/alphanumeric-sorting
Sample
class Program
{
static void Main()
{
string[] highways = new string[]
{
"100F",
"50F",
"SR100",
"SR9"
};
//
// We want to sort a string array called highways in an
// alphanumeric way. Call the static Array.Sort method.
//
Array.Sort(highways, new AlphanumComparatorFast());
//
// Display the results
//
foreach (string h in highways)
{
Console.WriteLine(h);
}
}
}
Output
50F
100F
SR9
SR100
Implementation
public class AlphanumComparatorFast : IComparer
{
public int Compare(object x, object y)
{
string s1 = x as string;
if (s1 == null)
{
return 0;
}
string s2 = y as string;
if (s2 == null)
{
return 0;
}
int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;
// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = s1[marker1];
char ch2 = s2[marker2];
// Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1];
int loc1 = 0;
char[] space2 = new char[len2];
int loc2 = 0;
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;
if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do
{
space2[loc2++] = ch2;
marker2++;
if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(space1);
string str2 = new string(space2);
int result;
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
int thisNumericChunk = int.Parse(str1);
int thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}
if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}

The simplest solution is to just left-pad the numerical values with a space to the same length.
List<string> lst = new List<string>
{
"Item 9",
"Item 999",
"Thing 999",
"Thing 5",
"Thing 1",
"Item 20",
"Item 10",
};
lst.Sort();
Output:
Item 9
Item 10
Item 20
Item 999
Thing 1
Thing 5
Thing 999
And you can always remove the extra white space used for padding after the sorting operation is performed.

You can use Sort with a Comparer like this:
List<string> q = new List<string>();
private void button_ekle_Click(object sender, EventArgs e)
{
for (int k=clb_input.Items.Count-1; k >= 0; k--)
{
if (clb_input.GetItemChecked(k) == true)
{
q.Add(clb_input.Items[k].ToString());
clb_input.Items.RemoveAt(k);
}
else { }
}
q.Sort((p1,p2)=>((int)(p1.split(" ")[1])).CompareTo((int)(p2.split(" ")[1])));
for (int t = 0; t < q.Count; t++)
{
clb_output.Items.Add(q[t].ToString());
}
}

Related

How to check if a string has numbers and letters of alphabet

I am wondering if strings have functionality that can see if a string contains words or words and other types of characters in general. (ideally I want to only consider strings that have readable sentences but that may too advanced) I am trying to count strings in a list that have that kind of text. I do not want to add to the count any strings that are only numbers, blank spaces, tabs or symbols (like ?, #, #) or even random letters unless there is at least some words contained in the string.
The strings will be initialised with "" so the code in the if statement - (this.ElementAt(i).getNotarised().ElementAt(j).getError() != "") - would be fine if there was no input at all. Unfortunately I am expecting some input in some case so this case also counts scenarios where there are only spaces, number, symbols or all of the 3 - which I do not want.
this is referring to a class inheriting from List
.getNotarised() returns a List
/** Find the highest/lowest amount of errors/solutions/suggestions/comments.
* If it's ASC then return the lowest to highest.
* If it's DESC then return the highest to lowest.
* Return a list with LOGS in the correct order.
* */
public List<LOG> highORlow(string asc_desc, string category)
{
int count = 0;
int[] array = new int[this.Count()];
for(int i = 0; i <= this.Count()-1; i++)
{
for(int j = 0; j <= this.ElementAt(i).getNotarised().Count()-1; j++)
{
if(this.ElementAt(i).getNotarised().ElementAt(j).getError() != "")
{
++count;
}
}
array[i] = count;
}
return new List<LOG>();
}
Point of interest:
string pattern = "[A-Za-z]{5}[0-9]{0}";
Regex xp = new Regex(pattern);
Use xp.IsMatch() to check if the pattern is matched.
/** Find the highest/lowest amount of errors/solutions/suggestions/comments.
* If it's true (ASC) then return the lowest to highest.
* If it's false (DESC) then return the highest to lowest.
* Return a list with LOGS in the correct order.
* */
public List<LOG> highORlow(bool asc_desc, string category)
{
int count = 0;
string pattern = "[A-Za-z]{5}[0-9]{0}";
Regex xp = new Regex(pattern);
Dictionary<LOG, int> array = new Dictionary<LOG, int>();
for (int i = 0; i <= this.Count() - 1; i++)
{
for (int j = 0; j <= this.ElementAt(i).getNotarised().Count() - 1; j++)
{
if (category == "ERROR")
{
if (xp.IsMatch(this.ElementAt(i).getNotarised().ElementAt(j).getError()))
{
++count;
continue;
}
}
if (category == "SOLUTION")
{
if (xp.IsMatch(this.ElementAt(i).getNotarised().ElementAt(j).getSolution()))
{
++count;
continue;
}
}
if (category == "SUGGESTION")
{
if (xp.IsMatch(this.ElementAt(i).getNotarised().ElementAt(j).getSuggestion()))
{
++count;
continue;
}
}
if (category == "COMMENT")
{
if (xp.IsMatch(this.ElementAt(i).getNotarised().ElementAt(j).getComment()))
{
++count;
continue;
}
}
}
array.Add(this.ElementAt(i), count);
count = 0;
}
List<LOG> list = new List<LOG>();
if(asc_desc)
{
foreach (KeyValuePair<LOG, int> v in array.OrderBy(key => key.Value))
{
list.Add(v.Key);
}
}
else
{
foreach (KeyValuePair<LOG, int> v in array.OrderByDescending(key => key.Value))
{
list.Add(v.Key);
}
}
return list;
}```

c# Splitting string to a multi dimensional array?

I encountered a little problem that i can't get my head around.
I have normal string[] array with a text import in it. Every row is stored in separate under every index.
A row usually look something like this:
string[i] = |title1|title2|title3|title4|title5|
string[i+1] = |Word1|Word2|Word3|Word4|Word5|
I want to split these rows and put them in a multi dimensional array.
Already counted how I have to declare the dimensions of the array.
I want to split it now. I was thinking about going through the normal array with two loops and look for the separator while saving the words in a string then copying it to the multi array.
Do you guys have any idea how could i do that, because this is too much hassle for such a small thing.
I want the multi array look something like this:
string[0,0] = title1,
string[0,1] = title2 etc.
string[1,0] = word1
string[1,1] = word2
This is the code that creates the array:
public string [,] SplitArrays(string [] arrayin)
{
long columns = 0;
char line = '|';
string row;
for(int i = 0; i < arrayin.Length;i++)
{
row = arrayin[i];
if (Convert.ToChar(row.Substring(0, 1)) == line)
{
for(int j = 0; j < row.Length;j++)
{
if (Convert.ToChar(row.Substring(j,(j+1))) == line)
{
columns++;
}
}
}
break;
}
int rowlength = arrayin.Length;
string[,] finalarray = new string[columns,rowlength];
And this is how far I got with separating, but I got kind of confuse and I probably messed it up:
int rowcolumncount = 0;
string word = "";
bool next = false;
for(int k = 0; k < arrayin.Length; k++)
{
row = arrayin[k];
for(int l = 0; l < row.Length; l++)
{
if (Convert.ToChar(row[l]) == line)
{
for(int z = 0; next == false;)
{
if(row[z] == line)
{
next = true;
}
else
{
string part = Convert.ToString(row[z]);
word = string.Join("",part);
}
finalarray[l, rowcolumncount] = word;
rowcolumncount++;
}
}
rowcolumncount = 0;
}
}
return finalarray;
}
The main array contains around 12000 lines.
Thank you!
You can try something like this: Split each item with arrayin by | and write these chunks into a line of 2D array:
public string[,] SplitArrays(string[] arrayin) {
if (null == arrayin)
return null;
else if (arrayin.Length <= 0)
return new string[0, 0];
// null : we don't know size (number of columns) before 1st line split
string[,] result = null;
int row = 0;
foreach (var line in arrayin) {
string[] items = line.Split('|');
// - 2 : skip the very first and the very last items which are empty
if (null == result)
result = new string[arrayin.Length, items.Length - 2];
// if line is too short, let's pad result with empty strings
for (int col = 0; col < result.GetLength(1); ++col)
result[row, col] = col + 1 < items.Length - 1
? items[col + 1]
: ""; // padding
row += 1;
}
return result;
}
Usage:
string[] source = new string[] {
"|title1|title2|title3|title4|title5|",
"|Word1|Word2|Word3|Word4|Word5|",
};
// {
// {"title1", "title2", "title3", "title4", "title5"},
// { "Word1", "Word2", "Word3", "Word4", "Word5"}
// }
string[,] array = SplitArrays(source);
If the number of items per line vary, you can use a jagged array.
We create the empty array and set the row count size.
Then we parse all lines of the list and for all line we split it to have desired items to add them into the dimension as we resize the row sub-array.
static public void Test()
{
var list = new string[]
{
"| title1 | title2 | title3 | title4 | title5 |",
"| Word1 | Word2 | Word3 | Word4 | Word5 |"
};
int indexD1 = 0;
string[][] result = null;
Array.Resize(ref result, list.Length);
foreach ( string item in list )
{
var str = item;
str = str.TrimStart('|').TrimStart();
str = str.TrimEnd('|').TrimEnd();
str = str.Replace(" | ", "|");
var items = str.Split('|');
Array.Resize(ref result[indexD1], items.Length);
int indexD2 = 0;
foreach ( string part in items )
result[indexD1][indexD2++] = part;
indexD1++;
}
foreach ( var row in result )
{
foreach ( var str in row )
Console.WriteLine(str);
Console.WriteLine();
}
}
You can also use a List of List of Strings and use the method Add():
var result = new List<List<string>>();

String list contains of all sub-components

String list G is :
[0] : {"1,2,5"}
[1] : {"1,2,4,5,6"}
[2] : {"2,4,6"}
[3] : {"1,4,6"}
With the following commands, we conclude that "1,4" exists in the List G[3] :
if (G[i].Contains("1,4")) { //code here }
How to modify the above commands, that in addition feature (Contains), "1,4" exists in the List G[1]?
Program codes
for (int i = 0; i < candid.Count; i++)
{
foreach (TransactionTP b in transactions)
{
string search = candid[i];
var searchNumbers = search.Split(',').Select(int.Parse).ToList();
for (int j = 0; j < G.Count; j++)
{
IEnumerable<int> numbers = G[j].Split(',').Select(int.Parse);
int idx = 0;
foreach (var number in numbers)
{
if (number == searchNumbers[idx])
{
idx++;
}
if (idx == searchNumbers.Count)
{
arraye[i] = arraye[i] + (b.transactionUtility);
break;
}
}
}
}
}
Update:
The order of the searched term matters.
In order to preserve the ordering of the set you are matching go (4,1 in this case), you will need to evaluate each string, keeping track of where you are in the match.
string[] G = new[]
{
"1,2,5",
"1,2,4,5,6",
"2,4,6",
"1,4,6"
};
string search = "1,4";
var searchNumbers = search.Split(',').Select(int.Parse).ToList();
for (int i = 0; i < G.Length; i++)
{
// Convert the string into an enumeration of numbers
IEnumerable<int> numbers = G[i].Split(',').Select(int.Parse);
// Index to keep track of the search
int idx = 0;
// Loop through the input set sequentially
foreach (var number in numbers)
{
// Check if the input matches the next expected number
if (number == searchNumbers[idx])
{
idx++;
}
if (idx == searchNumbers.Count)
{
Console.WriteLine("String {0} matched", G[i]);
break;
}
}
}
Replace your single Contains call with a method that does this:
bool passes = false;
for(int i = 0; i < G.Length; i++)
{
List<string> temp = new List<string>();
if(G[i].Contains(","))
{
temp = G[i].Split(",");
}
else
{
temp = G[i];
}
if(temp.Contains("1") && temp.Contains("4")
{
passes = true;
}
}
return passes;
This eliminates the possibility of matching on "10" or "41" etc etc
It also will not care if your elements are sorted, even though you said they are. It will also match regardless of the number of entries between "1" and "4" in the list.
You could spice this up to take any number of inputs that you want to match before qualifying as a match, I'll leave that to you if you want to make it so.

Array list is not being sorted properly when it contains string

I'm trying to sort a lists of alphanumeric values , in other words a list that contains numbers and strings
Example : BOB10, BOT20, ETC...
List<Object> myList = _items.OrderBy(x => x.FirstName).ToList();
_items= new List<Object>(myList);
But still the output is : BOT20 , BOB10
what is wrong?
First :
Try the alphanumeric algorithm approach ,
First implement the algorithm in a new class, than do this :
List<Object> yourList = new List < Object >(thePreviousList.OrderBy(x => x.FirstName, new AlphanumComparatorFast()).ToList())
As it was explained here :
http://www.dotnetperls.com/alphanumeric-sorting
Implement the algorithm AlphanumComparatorFast by creating a new class , than just call it
Edited :
The alpha algo below :
Public class AlphanumComparatorFast : IComparer
{
public int Compare(object x, object y)
{
string s1 = x as string;
if (s1 == null)
{
return 0;
}
string s2 = y as string;
if (s2 == null)
{
return 0;
}
int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;
// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = s1[marker1];
char ch2 = s2[marker2];
// Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1];
int loc1 = 0;
char[] space2 = new char[len2];
int loc2 = 0;
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;
if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do
{
space2[loc2++] = ch2;
marker2++;
if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(space1);
string str2 = new string(space2);
int result;
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
int thisNumericChunk = int.Parse(str1);
int thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}
if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}
Why not use List.Sort() instead of OrderBy?
using System;
using System.Collections.Generic;
namespace Test {
static class Program {
static void Main() {
List<string> list = new List<string>() {"BOT20", "BOB10", "BUG40", "BAG90"};
list.Sort();
foreach(var el in list) {
Console.Write(el + ">");
}
}
}
}
Outputs: BAG90>BOB10>BOT20>BUG40>

Creating a Score Method for an Array

My goal is to create a score method for a simple game based in an array. The game consists of putting 'R's or 'B's in one of 11 positions in an array. Once the array is full, the score method will execute as follows:
Any single 'R' or 'B' is worth 0 points
Any pair of 'R's or 'B's is worth 4 points
Any triple of 'R's or 'B's is worth 6 points
... and so on.
I'm having trouble calculating the score and I feel like I am missing something obvious so I'm coming here. The code I have looks for pairs and adds 2 to the score, but I end up missing 2 points (since the first pair is worth 4 and each additional "pair" is worth another 2).
public int score(char color)
{
int score = 0;
for (int i = 0; i < gameBoard.Length - 1; i++)
{
if (gameBoard[i] == color && gameBoard[i + 1] == color)
score += 2;
else
score += 0;
}
return score;
}
Your problem can be solved by regular expressions.
public int CalculateScore(char color)
{
var boardStr = String.Join("", gameBoard);
return GetCharacterSequences(color, boardStr)
.Sum(str => str.Length * 2);
}
//Returns same character sequences of length more than two.
private IEnumerable<string> GetCharacterSequences(char color, string boardStr)
{
return Regex.Matches(boardStr, $#"({color})\1+").OfType<Match>()
.SelectMany(match => match.Groups.OfType<Group>())
.Select(#group => #group.Value)
.Where(str => str.Length > 1);
}
Idea of sum function: Each pair costs 4 points, each triple costs 6 points hence each single char costs 2 points.
GetCharacterSequences looks little bit complex but it makes CalculateScore method very simple.
Well you could create a boolean variable and use that to determine the number of points added.
Note: This code doesn't account for triple matches as you've mentioned in your question.
public int score(char color)
{
int score = 0;
bool firstMatch = true;
for (int i = 0; i < gameBoard.Length - 1; i++)
{
if (gameBoard[i] == color && gameBoard[i + 1] == color) {
if (firstMatch == true) {
score += 4;
firstMatch = false;
} else {
score += 2;
}
}
else
{
score += 0;
}
}
return score;
}
Here's another approach, based on a pair being 4 points, 3 being 6 points, and assuming 4 being 8 points and so forth (basically number of adjacent colors * 2 if there is at least a pair).
public int score(char color)
{
int adj = 0;
int score = 0;
for (int i = 0; i < gameBoard.Length; i++)
{
if (gameBoard[i] == color)
{
adj++;
}
else
{
if (adj > 1)
{
score += adj * 2;
}
adj = 0;
}
}
}
This code loops through the array, and keeps a count of the number of adjacent spots marked by the specified color. When it comes to an element of the array that is not the right color, it checks to see how many adjacent elements had the correct color.
If it's greater than 1, then it multiplies the number of adjacent elements by 2 and resets the adjacent elements counter to 0 and continues through the array.
Given an array of 'R', 'R', 'B', 'B', 'B', 'R', 'R', 'R', 'B', 'R', 'B', it produces a score of 10 for 'R' (4 + 6) and 6 for 'B' (6), as follows:
'R', 'R' = 4
'R', 'R', 'R' = 6
'R' = 0
'B', 'B', 'B' = 6
'B' = 0
Here is what i came up with
namespace TestApp1
{
class Program
{
static void Main(string[] args)
{
string[] test = GetValues();
string testView = String.Join(String.Empty, test);
string score = GetScore(test).ToString();
Console.WriteLine(testView);
Console.WriteLine(score);
Console.ReadLine();
}
public static int GetScore(string[] test)
{
int score = 0;
int occurence = 0;
string LastChar = string.Empty;
for (int i = 0; i < test.Length; i++)
{
if(LastChar == string.Empty)
{
LastChar = test[i];
occurence += 1;
continue;
}
if(LastChar == test[i])
{
occurence += 1;
if(i == test.Length - 1)
{
if (occurence > 1)
{
score += occurence * 2;
}
}
}
else
{
if(occurence > 1)
{
score += occurence * 2;
}
LastChar = test[i];
occurence = 1;
}
}
return score;
}
public static string[] GetValues()
{
List<string> values = new List<string>();
for (int i = 0; i < 12; i++)
{
var rnd = new Random(DateTime.Now.Millisecond);
int ticks = rnd.Next(0, 2);
values.Add(ticks == 1 ? "R" : "B");
System.Threading.Thread.Sleep(2);
}
return values.ToArray();
}
}
}
To calculate each score independently
namespace TestApp1
{
class Program
{
static void Main(string[] args)
{
string[] test = GetValues();
string testView = String.Join(String.Empty, test);
int rScore = 0;
int bScore = 0;
GetScore(test,out rScore, out bScore);
string score = "R: " + rScore.ToString() + " B: " + bScore.ToString();
Console.WriteLine(testView);
Console.WriteLine(score);
Console.ReadLine();
}
public static void GetScore(string[] test, out int rScore, out int bScore)
{
int occurence = 0;
string LastChar = string.Empty;
rScore = 0;
bScore = 0;
for (int i = 0; i < test.Length; i++)
{
if(LastChar == string.Empty)
{
LastChar = test[i];
occurence += 1;
continue;
}
if(LastChar == test[i])
{
occurence += 1;
if(i == test.Length - 1)
{
if (occurence > 1)
{
if(LastChar == "R")
{
rScore += occurence * 2;
}
else
{
bScore += occurence * 2;
}
}
}
}
else
{
if(occurence > 1)
{
if (LastChar == "R")
{
rScore += occurence * 2;
}
else
{
bScore += occurence * 2;
}
}
LastChar = test[i];
occurence = 1;
}
}
}
public static string[] GetValues()
{
List<string> values = new List<string>();
for (int i = 0; i < 12; i++)
{
var rnd = new Random(DateTime.Now.Millisecond);
int ticks = rnd.Next(0, 2);
values.Add(ticks == 1 ? "R" : "B");
System.Threading.Thread.Sleep(2);
}
return values.ToArray();
}
}
}

Categories