I have a problem with code.
I want to implement radix sort to numbers on c# by using ling.
I give as input string and give 0 in the start of the number:
foreach(string number in numbers){<br>
if(number.lenth > max_lenth) max_lenth = number.lenth;<br><br>
for(int i = 0; i < numbers.lenth; i++){<br>
while(numbers[i].length < max_lenth) numbers[i] = "0" + numbers[i];<br><br>
after this I need to order by each number in string, but it doesn't work with my loop:
var output = input.OrderBy(x => x[0]);
for (int i = 0; i < max_lenth; i++)
{
output = output.ThenBy(x => x[i]);
}
so I think to create a string that contain "input.OrderBy(x => x[0]);" and add "ThenBy(x => x[i]);"
while max_length - 1 and activate this string that will my result.
How can I implement it?
I can't use ling as a tag because its my first post
If I am understanding your question, you are looking for a solution to the problem you are trying to solve using Linq (by the way, the last letter is a Q, not a G - which is likely why you could not add the tag).
The rest of your question isn't quite clear to me. I've listed possible solutions based on various assumptions:
I am assuming your array is of type string[]. Example:
string[] values = new [] { "708", "4012" };
To sort by alpha-numeric order regardless of numeric value order:
var result = values.OrderBy(value => value);
To sort by numeric value, you can simply change the delegate used in the OrderBy clause assuming your values are small enough:
var result = values.OrderBy(value => Convert.ToInt32(value));
Let us assume though that your numbers are arbitrarily long, and you only want the values in the array as they are (without prepending zeros), and that you want them sorted in integer value order:
string[] values = new [] { "708", "4012" };
int maxLength = values.Max(value => value.Length);
string zerosPad = string
.Join(string.Empty, Enumerable.Repeat<string>("0", maxLength));
var resultEnumerable = values
.Select(value => new
{
ArrayValue = value,
SortValue = zerosPad.Substring(0, maxLength - value.Length) + value
}
)
.OrderBy(item => item.SortValue);
foreach(var result in resultEnumerable)
{
System.Console.WriteLine("{0}\t{1}", result.SortValue, result.ArrayValue);
}
The result of the above code would look like this:
0708 708
4012 4012
Hope this helps!
Try Array.Sort(x) or Array.Sort(x,StringComparer.InvariantCulture).
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
For example
private string[] oras = { "07:00AM", "07:30AM", "08:00AM", "08:30AM", "09:00AM", "10:00AM", " 10:30AM", "11:00AM", "11:30AM", "12:00PM", "12:30PM", "01:00PM", "01:30PM", "02:00PM", "02:30PM", "03:00PM", "03:30PM", "04:00PM", "04:30PM", "05:00PM", "05:30PM", "06:00PM", "06:30PM", "07:00PM", "07:30PM", "08:00PM" };
i want to remove items between "7:00AM" and "10:30AM"
Steps:
Convert to TimeSpan;
Filter where the time span doesn't meet your criteria;
Optionally convert back to string and/or an array.
Code:
var orasTs = oras.Select(o => ParseTimeSpan(o))
.Where(ts => !(ts.TotalHours >= 7 && ts.TotalHours <= 10.5f))
.ToArray();
private static TimeSpan ParseTimeSpan(string t)
{
int hoursToAdd = 0;
if (t.EndsWith("AM"))
{
hoursToAdd = 0;
}
else
{
hoursToAdd = 12;
}
return TimeSpan.Parse(t.Substring(0, 5)).Add(TimeSpan.FromHours(hoursToAdd));
}
You can try this. What I am doing here is that I am converting first the time and select based on your condition. Then return the result that should not match on the first result:
string[] oras = { "07:00AM", "07:30AM", "08:00AM", "08:30AM", "09:00AM", "10:00AM", " 10:30AM", "11:00AM", "11:30AM", "12:00PM", "12:30PM", "01:00PM", "01:30PM", "02:00PM", "02:30PM", "03:00PM", "03:30PM", "04:00PM", "04:30PM", "05:00PM", "05:30PM", "06:00PM", "06:30PM", "07:00PM", "07:30PM", "08:00PM" };
var res = oras.Where(c => DateTime.Parse(c) >= DateTime.Parse("07:00AM") && DateTime.Parse(c) <= DateTime.Parse("10:30AM"))
.ToArray();
var result = oras.Where(b => !res.Any(c => c == b)).ToArray();
Short answer, no. You cannot "Remove" or "Add" to arrays directly.
You can however use Array.Resize to resize the referenced array, or just straight up make a new one with a different element count.
This is not however answering the question you are ACTUALLY asking, for any given set of strings, how do you remove, conditionally, a portion of them.
I will assume going forwards you are/can use linq (System.Linq) and we will use oras.
Is it Time?
Given your example data, if you know they will all be "time" strings, you should parse to a strongly typed time object, probably TimeSpan like this
var myTimeSpans = oras.Select(o => TimeSpan.Parse(o));
using your new list of TimeSpan instances, you can then select only the elements that you do want using a Linq Where statement.
var myWantedTimespans = myTimeSpans.Where(ts => ts.TotalHours < 7 || ts.TotalHours > 10.5f);
This will get all TimeSpans where the total hours are lower than 7, or greater than 10.5. Modify as required for different conditional requirements.
you can then further utilise them in a strongly typed fashion in your new collection, or cast them back to string for whatever stringly typed purposes you may have.
Is it just strings?
If it's just arbitrary string values in an array, we can still get a theoretical Range removed, but it's a bit more messy. If you take the strings at face value, you can get a Range removed by using the base string comparer. For example:
string lower = "07:00AM";
string upper = "10:30AM";
var newArray = oras.Where(f => string.Compare(f, lower) < 0 || string.Compare(f, upper) > 0).ToArray();
this will remove a string Range between 2 other string values. however this is taking all strings at face value, where their content is based on their character values individually and compared in that fashion. if there is any data that could be considered if it was in a strongly typed fashion this will not be considered in a string-only comparison.
Hope this helps.
Looks like you are having fun here :)
Here's my realization:
string format = "hh:mmtt";
DateTime excludeStart = DateTime.ParseExact("07:00AM", format, CultureInfo.InvariantCulture),
excludeEnd = DateTime.ParseExact("10:30AM", format, CultureInfo.InvariantCulture);
var result = oras
.Select(str => DateTime.ParseExact(str.Trim(), format, CultureInfo.InvariantCulture))
.Where(ts => ts < excludeStart || ts > excludeEnd)
.Select(ts => ts.ToString("hh:mmtt"))
.ToArray();
You can run it here
Another possible variant:
Convert your list items to DateTime
Calculate count of items for given range
Calculate the range's start element index
Use RemoveRange() method, after making list from your array
Convert back to string array
string[] oras = { "07:00AM", "07:30AM", "08:00AM", "08:30AM", "09:00AM", "10:00AM", " 10:30AM", "11:00AM", "11:30AM", "12:00PM", "12:30PM", "01:00PM", "01:30PM", "02:00PM", "02:30PM", "03:00PM", "03:30PM", "04:00PM", "04:30PM", "05:00PM", "05:30PM", "06:00PM", "06:30PM", "07:00PM", "07:30PM", "08:00PM" };
int elementsCount = oras.Select(DateTime.Parse)
.Count(c => c >= DateTime.Parse("07:00AM")
&& c <= DateTime.Parse("10:30AM"));
int startIndex = Array.IndexOf(oras, "07:00AM");
List<string> orasList = oras.ToList();
orasList.RemoveRange(startIndex, elementsCount);
oras = orasList.ToArray();
If you can use List, you can use List.RemoveRange
(MSDN)
List<string> listHour = new List<string>();
foreach(var item in oras){
listHour.Add(item);
}
listHour.RemoveRange(1,4);
oras = listHour.ToArray();
I have a list of strings, each containing a number substring, that I'd like to be reordered based on the numerical value of that substring. The set will look something like this, but much larger:
List<string> strings= new List<string>
{
"some-name-(1).jpg",
"some-name-(5).jpg",
"some-name-(5.1).jpg",
"some-name-(6).jpg",
"some-name-(12).jpg"
};
The number will always be surrounded by parentheses, which are the only parentheses in the string, so using String.IndexOf is reliable. Notice that not only may there be missing numbers, but there can also be decimals, not just integers.
I'm having a really tough time getting a reordered list of those same strings that has been ordered on the numerical value of that substring. Does anyone have a way of doing this, hopefully one that performs well? Thanks.
This will check if the items between the parenthesis is convertible to a double, if not it will return -1 for that case.
var numbers = strings.Select( x => x.Substring( x.IndexOf( "(" ) + 1,
x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) ).Select( x =>
{
double val;
if( double.TryParse( x, out val ) ) {
return val;
}
// Or whatever you want to do
return -1;
} ).OrderBy( x => x ); // Or use OrderByDescending
If you are sure there will always be a number between the parenthesis, then use this as it is shorter:
var numbers = strings.Select(
x => x.Substring( x.IndexOf( "(" ) + 1, x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) )
.Select( x => double.Parse(x))
.OrderBy( x => x ); // Or use OrderByDescending
EDIT
I need the original strings, just ordered on those numbers.
Basically what you need to do is to pass a predicate to the OrderBy and tell it to order by the number:
var items = strings.OrderBy(
x => double.Parse( x.Substring( x.IndexOf( "(" ) + 1,
x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) ));
How about an OO approach?
We are ordering string but we need to treat them like numbers. Wouldn't it be nice if there was a way we can just call OrderBy and it does the ordering for us? Well there is. The OrderBy method will use the IComparable<T> if there is one. Let's create a class to hold our jpg paths and implement the IComparable<T> interface.
public class CustomJpg : IComparable<CustomJpg>
{
public CustomJpg(string path)
{
this.Path = path;
}
public string Path { get; private set; }
private double number = -1;
// You can even make this public if you want.
private double Number
{
get
{
// Let's cache the number for subsequent calls
if (this.number == -1)
{
int myStart = this.Path.IndexOf("(") + 1;
int myEnd = this.Path.IndexOf(")");
string myNumber = this.Path.Substring(myStart, myEnd - myStart);
double myVal;
if (double.TryParse(myNumber, out myVal))
{
this.number = myVal;
}
else
{
throw new ArgumentException(string.Format("{0} has no parenthesis or a number between parenthesis.", this.Path));
}
}
return this.number;
}
}
public int CompareTo(CustomJpg other)
{
if (other == null)
{
return 1;
}
return this.Number.CompareTo(other.Number);
}
}
What is nice about the above approach is if we keep calling OrderBy, it will not have to search for the opening ( and ending ) and doing the parsing of the number every time. It caches it the first time it is called and then keeps using it. The other nice thing is that we can bind to the Path property and also to the Number (we would have to change the access modifier from private). We can even introduce a new property to hold the thumbnail image and bind to that as well. As you can see, this approach is far more flexible, clean and an OO approach. Plus the code for finding the number is in one place so if we switch from () to another symbol, we would just change it in one place. Or we can modify to look for () first and if not found look for another symbol.
Here is the usage:
List<CustomJpg> jpgs = new List<CustomJpg>
{
new CustomJpg("some-name-(1).jpg"),
new CustomJpg("some-name-(5).jpg"),
new CustomJpg("some-name-(5.1).jpg"),
new CustomJpg("some-name-(6).jpg"),
new CustomJpg("some-name-(12).jpg")
};
var ordered = jpgs.OrderBy(x => x).ToList();
You can use this approach for any object.
In the above example code return a list of numbers ordered by numbers but if you want have list of file names that ordered by name better you put in same zero to beginning of the numbers like "some-name-(001).jpg" and you can simply
order that
List<string> strings = new List<string>
{
"some-name-(001).jpg",
"some-name-(005.1).jpg",
"some-name-(005).jpg",
"some-name-(004).jpg",
"some-name-(006).jpg",
"some-name-(012).jpg"
};
var orederedByName =strings.Select(s =>s ).OrderBy(s=>s);
You can make the Substring selection easier, if you first cut off the part starting at the closing parenthesis ")". I.e., from "some-name-(5.1).jpg" you first get "some-name-(5.1". Then take the part after "(". This saves a length calculation, as the 2nd Substring automatically takes everything up to the end of string.
strings = strings
.OrderBy(x => Decimal.Parse(
x.Substring(0, x.IndexOf(")"))
.Substring(x.IndexOf("(") + 1)
)
)
.ToList();
This is probably not of great importance here, but generally, decimal stores numbers given in decimal notation more accurately than double. double can convert 17.2 as 17.19999999999999.
I just created a simple method to count occurences of each character within a string, without taking caps into account.
static List<int> charactercount(string input)
{
char[] characters = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
input = input.ToLower();
List<int> counts = new List<int>();
foreach (char c in characters)
{
int count = 0;
foreach (char c2 in input) if (c2 == c)
{
count++;
}
counts.Add(count);
}
return counts;
}
Is there a cleaner way to do this (i.e. without creating a character array to hold every character in the alphabet) that would also take into account numbers, other characters, caps, etc?
Conceptually, I would prefer to return a Dictionary<string,int> of counts. I'll assume that it's ok to know by omission rather than an explicit count of 0 that a character occurs zero times, you can do it via LINQ. #Oded's given you a good start on how to do that. All you would need to do is replace the Select() with ToDictionary( k => k.Key, v => v.Count() ). See my comment on his answer about doing the case insensitive grouping. Note: you should decide if you care about cultural differences in characters or not and adjust the ToLower method accordingly.
You can also do this without LINQ;
public static Dictionary<string,int> CountCharacters(string input)
{
var counts = new Dictionary<char,int>(StringComparer.OrdinalIgnoreCase);
foreach (var c in input)
{
int count = 0;
if (counts.ContainsKey(c))
{
count = counts[c];
}
counts[c] = counts + 1;
}
return counts;
}
Note if you wanted a Dictionary<char,int>, you could easily do that by creating a case invariant character comparer and using that as the IEqualityComparer<T> for a dictionary of the required type. I've used string for simplicity in the example.
Again, adjust the type of the comparer to be consistent with how you want to handle culture.
Using GroupBy and Select:
aString.GroupBy(c => c).Select(g => new { Character = g.Key, Num = g.Count() })
The returned anonymous type list will contain each character and the number of times it appears in the string.
You can then filter it in any way you wish, using the static methods defined on Char.
Your code is kind of slow because you are looping through the range a-z instead of just looping through the input.
If you only need to count letters (like your code suggests), the fastest way to do it would be:
int[] CountCharacters(string text)
{
var counts = new int[26];
for (var i = 0; i < text.Length; i++)
{
var charIndex - text[index] - (int)'a';
counts[charIndex] = counts[charindex] + 1;
}
return counts;
}
Note that you need to add some thing like verify the character is in the range, and convert it to lowercase when needed, or this code might throw exceptions. I'll leave those for you to add. :)
Based on +Ran's answer to avoiding IndexOutOfRangeException:
static readonly int differ = 'a';
int[] CountCharacters(string text) {
text = text.ToLower();
var counts = new int[26];
for (var i = 0; i < text.Length; i++) {
var charIndex = text[i] - differ;
// to counting chars between 'a' and 'z' we have to do this:
if(charIndex >= 0 && charIndex < 26)
counts[charIndex] += 1;
}
return counts;
}
Actually using Dictionary and/or LINQ is not optimized enough as counting chars and working with a low level array.
string dosage = "2/3/5 mg";
string[] dosageStringArray = dosage.Split('/');
int[] dosageIntArray = null;
for (int i = 0; i <= dosageStringArray.Length; i++)
{
if (i == dosageStringArray.Length)
{
string[] lastDigit = dosageStringArray[i].Split(' ');
dosageIntArray[i] = Common.Utility.ConvertToInt(lastDigit[0]);
}
else
{
dosageIntArray[i] = Common.Utility.ConvertToInt(dosageStringArray[i]);
}
}
I am getting the exception on this line: dosageIntArray[i] = Common.Utility.ConvertToInt(dosageStringArray[i]);
I am unable to resolve this issue. Not getting where the problem is. But this line int[] dosageIntArray = null; is looking suspicious.
Exception: Object reference not set to an instance of an object.
The biggest problem with your solution is not the missing array declaration, but rather how
you'd parse the following code:
string dosage = "2/13/5 mg";
Since your problem is surely domain specific, this may not arise, but some variation of two digits representing same integer.
The following solution splits the string on forward slash, then removes any non-digits from the substrings before converting them to integers.
Regex digitsOnly = new Regex(#"[^\d]");
var array = dosage.Split('/')
.Select(num => int.Parse(digitsOnly.Replace(num, string.Empty)))
.ToArray();
Or whatever that looks like with the cuddly Linq synthax.
You are looking for something like
int[] dosageIntArray = new int[dosageStringArray.Length];
You are trying to access a null array (dosageIntArray) here:
dosageIntArray[i] = Common.Utility.ConvertToInt(lastDigit[0]);
You need to initialize it before you can access it like that.
You have to allocate dosageIntArray like this:
in[] dosageIntArray = new int[dosageStringArray.Length];
Also, you have another bug in your code:
Index of last element of an array is Length - 1.
Your for statement should read as:
for (int i = 0; i < dosageStringArray.Length; i++)
or
for (int i = 0; i <= (dosageStringArray.Length - 1); i++)
The former is preferred and is the most common style you will see.
I strongly recommend you use Lists instead of Arrays. You don't need to define the size of the List; just add items to it. It's very functional and much easier to use.
As alternative approach:
var dosage = "2/3/5 mg";
int[] dosageIntArray = Regex.Matches(dosage, #"\d+")
.Select(m => int.Parse(m.Value))
.ToArray();
So what I'm trying to do, is take a job number, which looks like this xxx123432, and count the digits in the entry, but not the letters. I want to then assign the number of numbers to a variable, and use that variable to provide a check against the job numbers to determine if they are in a valid format.
I've already figured out how to perform the check, but I have no clue how to go about counting the numbers in the job number.
Thanks so much for your help.
Using LINQ :
var count = jobId.Count(x => Char.IsDigit(x));
or
var count = jobId.Count(Char.IsDigit);
int x = "xxx123432".Count(c => Char.IsNumber(c)); // 6
or
int x = "xxx123432".Count(c => Char.IsDigit(c)); // 6
The difference between these two methods see at Char.IsDigit and Char.IsNumber.
Something like this maybe?
string jobId = "xxx123432";
int digitsCount = 0;
foreach(char c in jobId)
{
if(Char.IsDigit(c))
digitsCount++;
}
And you could use LINQ, like this:
string jobId = "xxx123432";
int digitsCount = jobId.Count(c => char.IsDigit(c));
string str = "t12X234";
var reversed = str.Reverse().ToArray();
int digits = 0;
while (digits < str.Length &&
Char.IsDigit(reversed[digits])) digits++;
int num = Convert.ToInt32(str.Substring(str.Length - digits));
This gives num 234 as output if that is what you need.
The other linq/lambda variants just count characters which I think is not completely correct if you have a string like "B2B_MESSAGE_12344", because it would count the 2 in B2B.
But I'm not sure if I understood correctly what number of numbers means. Is it the count of numbers (other answers) or the number that numbers form (this answer).