What I'm trying to do is to create a function that will rearrange a string of numbers like "1234" to "4321". I'm certain that there are many much more efficient ways to do this than my method but I just want to see what went wrong with what I did because I'm a beginner at programming and can use the knowledge to get better.
My thought process for the code was to:
find the largest number in the inputted string
add the largest number into a list
remove the largest number from the inputted string
find the largest number again from the (now shorter) string
So I made a function that found the largest number in a string and it worked fine:
static int LargestNumber(string num)
{
int largestnumber = 0;
char[] numbers = num.ToCharArray();
foreach (var number in numbers)
{
int prevNumber = (int) char.GetNumericValue(number);
if (prevNumber >= largestnumber)
{
largestnumber = prevNumber;
}
}
return largestnumber;
}
Now the rearranging function is what I am having problems with:
static List<int> Rearrange(string num)
{
List<int> rearranged = new List<int>(); // to store rearranged numbers
foreach (var number in num) //for every number in the number string
{
string prevnumber = number.ToString(); // the previous number in the loop
if (prevnumber == LargestNumber(num).ToString()) // if the previous number is the larges number in the inputted string (num)
{
rearranged.Add(Convert.ToInt32(prevnumber)); // put the previous number into the list
// removing the previous number (largest) from the inputted string and update the inputted string (which should be now smaller)
StringBuilder sb = new StringBuilder(num);
sb.Remove(num.IndexOf(number), 1);
num = sb.ToString();
}
}
return rearranged; // return the final rearranged list of numbers
}
When I run this code (fixed for concatenation):
var rearranged = Rearrange("3250");
string concat = String.Join(" ", rearranged.ToArray());
Console.WriteLine(concat);
All I get is:
5
I'm not sure what I'm missing or what I'm doing wrong - the code doesn't seem to be going back after removing '5' which i s the highest number then removing the next highest number/
Your issue is your if statement within your loop.
if (prevnumber == LargestNumber(num).ToString()
{
rearranged.Add(Convert.ToInt32(prevnumber));
//...
}
You only ever add to your List rearranged if the value of prevnumber is the largest value, which is false for every number but 5, so the only value that ever gets added to the list is 5.
That's the answer to why it's only returning 5, but I don't think that will make your method work properly necessarily. You're doing a very dangerous thing by changing the value of the collection you are iterating over (the characters in num) from within the loop itself. Other answers have been written for you containing a method that rearranges the numbers as you've described.
Your Rearrange method is returning List<int> when you try to write that to the console, the best it can do is write System.Collections.Generic.List1[System.Int32] (its type)
Instead of trying to write the list, convert it first into a data type that can be written (string for example)
eg:
var myList = Rearrange("3250");
string concat = String.Join(" ", myList.ToArray());
Console.WriteLine(concat);
Building on pats comment you could iterate through your list and write them to the console.
e.g.
foreach(var i in Rearrange(3250))
{
console.writeline(i.ToString());
}
or if you want to see the linq example.
using system.linq;
Rearrange(3250).foreach(i => console.writeline(i.ToString()));
--edit after seeing you're only getting '5' output
This is because your function only adds number to the list if they are the largest number in your list, which is why 5 is only being added and returned.
Your Rearrange method can be written easily using Array.Sort (or similar with (List<T>) :
int[] Rearrange(int num)
{
var arr = num.ToString ().ToCharArray ();
Array.Sort (arr, (d1, d2) => d2 - d1);
return Array.ConvertAll (arr, ch => ch - '0');
}
Just reading your first sentence
Does not test for integers
static int ReversedNumber(string num)
{
char[] numbers = num.ToCharArray();
Array.Sort(numbers);
Array.Reverse(numbers);
Debug.WriteLine(String.Concat(numbers));
return (int.Parse(String.Concat(numbers)));
}
Because your foreach loop within Rearrange method only loop through the original num. The algorithm doesn't continue to go through the new num string after you have removed the largest number.
You can find the problem by debugging, this foreach loop in Rearrange goes only 4 times if your input string is "3250".
Related
I have a recursion assignment where I have to input into console the following data:
On the first line a natural number equal with the number of tiles I have to input on the following lines (in my example the first number is 6).
On the following lines the domino tiles in the following order:
1 2
1 3
3 4
2 3
3 5
4 5
On the last line another number representing the number of tiles that needs to be returned on each separate line (in my example the number is equal with 3).
With this data I have to display all possible combinations of pairs. For each separate line the number the second number from the first pair has to be equal with the first number of the following pair and so on. I had a hint for this assignment where I have to declare an intermediary list that is equal with the function (using recursion from the start) but when I'm trying to run the code it gives me a null exception for the intermediaryList.
In the first instance I read the data as a 2d array but settled with a simple string array where.
This is my code so far, could you help me with a suggestion, how to avoid the null exception?
(sorry if I missed something from the explanation, this is my second post here so far and thank you in advance). Also I have to mention that I'm allowed to use just "using System;" Not allowed to use Linq or anything else.
enter code here
static string[] ReadDominoTiles(int n)
{
string[] input = new string[n];
for (int i = 0; i < n; i++)
{
input[i] = Console.ReadLine();
}
return input;
}
static string[] DominoSolution(string[] dominoTiles, int numberOfPairs)
{
string[] finalList = new string[numberOfPairs];
if (numberOfPairs == 1)
{
return finalList;
}
string[] intermediaryList = DominoSolution(dominoTiles, numberOfPairs - 1);
for (int i = 0; i < intermediaryList.Length; i++)
{
for (int j = 0; j < dominoTiles.Length; j++)
{
// This is where I get the nullref exception, every value from intermediaryList is null
if (intermediaryList[i] != dominoTiles[j] && intermediaryList[j][1] == dominoTiles[j][0])
{
finalList[j] += intermediaryList[j] + " " + dominoTiles[j];
}
}
}
return finalList;
}
static void Main(string[] args)
{
int n = Convert.ToInt32(Console.ReadLine());
string[] dominoTiles = ReadDominoTiles(n);
int numberOfPairs = Convert.ToInt32(Console.ReadLine());
Console.WriteLine(DominoSolution(dominoTiles, numberOfPairs));
}
This is where I get the nullref exception, every value from intermediaryList is null
That's correct, you call the function recursively after doing exactly zero work, besides adding a recursion termination condition which returns an array of nulls. So the first time you come out of your recursion you have in intermediaryList a number of null elements, so when you pretend they're strings and try to get intermediaryList[j][1] you'll get a null reference exception.
As to the solution, it's not exactly clear what's with all the arrays you're allocating. Use a List<> with an actual type and do your work properly. Though I hate to break it to you but if I understand your assignment correctly the solution will use backtracking and combinatorics, which is much more code and much better structured than what you have here.
I want to trim all the white-spaces and empty strings only from the starting and ending of an array without converting it into a string in C#.
This is what I've done so far to solve my problem but I'm looking for a bit more efficient solution as I don't want to be stuck with a just works solution to the prob
static public string[] Trim(string[] arr)
{
List<string> TrimmedArray = new List<string>(arr);
foreach (string i in TrimmedArray.ToArray())
{
if (String.IsEmpty(i)) TrimmedArray.RemoveAt(TrimmedArray.IndexOf(i));
else break;
}
foreach (string i in TrimmedArray.ToArray().Reverse())
{
if (String.IsEmpty(i)) TrimmedArray.RemoveAt(TrimmedArray.IndexOf(i));
else break;
}
return TrimmedArray.ToArray();
}
NOTE: String.IsEmpty is a custom function which check whether a string was NULL, Empty or just a White-Space.
Your code allocates a lot of new arrays unnecessarily. When you instantiate a list from an array, the list creates a new backing array to store the items, and every time you call ToArray() on the resulting list, you're also allocating yet another copy.
The second problem is with TrimmedArray.RemoveAt(TrimmedArray.IndexOf(i)) - if the array contains multiple copies of the same string value in the middle as at the end, you might end up removing strings from the middle.
My advice would be split the problem into two distinct steps:
Find both boundary indices (the first and last non-empty strings in the array)
Copy only the relevant middle-section to a new array.
To locate the boundary indices you can use Array.FindIndex() and Array.FindLastIndex():
static public string[] Trim(string[] arr)
{
if(arr == null || arr.Length == 0)
// no need to search through nothing
return Array.Empty<string>();
// define predicate to test for non-empty strings
Predicate<string> IsNotEmpty = string s => !String.IsEmpty(str);
var firstIndex = Array.FindIndex(arr, IsNotEmpty);
if(firstIndex < 0)
// nothing to return if it's all whitespace anyway
return Array.Empty<string>();
var lastIndex = Array.FindLastIndex(arr, IsNotEmpty);
// calculate size of the relevant middle-section from the indices
var newArraySize = lastIndex - firstIndex + 1;
// create new array and copy items to it
var results = new string[newArraySize];
Array.Copy(arr, firstIndex, results, 0, newArraySize);
return results;
}
I like the answer by Mathias R. Jessen as it is efficient and clean.
Just thought I'd show how to do it using the List<> as in your original attempt:
static public string[] Trim(string[] arr)
{
List<string> TrimmedArray = new List<string>(arr);
while (TrimmedArray.Count>0 && String.IsEmpty(TrimmedArray[0]))
{
TrimmedArray.RemoveAt(0);
}
while (TrimmedArray.Count>0 && String.IsEmpty(TrimmedArray[TrimmedArray.Count - 1]))
{
TrimmedArray.RemoveAt(TrimmedArray.Count - 1);
}
return TrimmedArray.ToArray();
}
This is not as efficient as the other answer since the internal array within the List<> has to shift all its elements to the left each time an element is deleted from the front.
I've been working on an application that generates Latitudes and Longitudes, and I now require the ability to find the largest and smallest values to determine the upper and lower limits (and east / west limits) of the data set.
I've been trying to sort them using the Enumerable.Min method, but it should appear to not support numbers below -100.0000.
Have I made a mistake with my code? How can get the largest and smallest numbers in the most efficient way (lists have around 150,000+ entries).
public static void Main()
{
var arr = new string[]{"-88.98559", "-94.98711", "-95.79591", "-98.04622", "100.0001", "-101.57691", "-110.00614"};
var arrList = new List<string>(){};
foreach (var value in arr)
{
arrList.Add(value);
}
Console.WriteLine(arrList.Min().ToString()); //Result : 100.0001, should be -110.00614
Console.WriteLine(arrList.Max().ToString()); //Result : -98.04622
}
looks like your problem is that you're using min/max on string values. if you're comparing the string "100" vs "90" then "100" is smaller because 1 < 9. (no different than "alice" vs "bob". a < b even though "bob" is shorter)
if you want to do numeric comparisons, then you have to make arrList an array of floats or something, and float.parse(value) before you add to the list.
Of course the simple answer was to store my values as doubles (and adjust my generating code to not convert to string). Thanks all for the nudge!
public static void Main()
{
var arr = new double[]{-88.98559, -94.98711, -95.79591, -98.04622, 100.0001, -101.57691, -110.00614};
var arrList = new List<double>(){};
foreach (var value in arr)
{
arrList.Add(value);
}
Console.WriteLine(arrList.Min().ToString());
Console.WriteLine(arrList.Max().ToString());
}
I have a problem my code is not efficient enough. He thinks he knows the content. How do I write the code so it can work with any file. So he practically only excludes numbers and ignores the words (strings).
public static int SumUpFileContent(string file)
{
int sum = 0;
var lines = File.ReadAllLines(file);
foreach (var line in lines)
{
if (int.TryParse(line, out int i))
sum += i;
}
return sum;
}
Keep in mind :
This doesn't work with numbers that have decimals, only integers. replace int.TryParse() with double.TryParse() if you have to.
The data must come in a very specific format (i.e. every entry must be on its own line)
From the information you provided you could split the file content in to an array
then for each item in the array use an int.tryParse to see if it is a number. (this is assumed that the numbers are always int)
public static void FilterLockedThreads(List<string> Links, List<string> LockedLinks)
{
string number;
string number1;
for (int i = 0; i < Links.Count; i++)
{
number = Links[i].Substring(32, 6);
for (int x = 0; x < LockedLinks.Count; x++)
{
number1 = LockedLinks[x].Substring(61, 6);
if (Links[i].Contains(LockedLinks[x]))
{
if (number == number1)
{
string identical = number;
}
}
}
}
}
I used a breakpoint and there are numbers that are part of an item in LockedLinks that also exist in Links.
Both Lists LockedLinks and Links are
For example in Links i have 50 items and in LockedLinks 7 items. For example in Links the item in index 32 is:
http://rotter.net/forum/scoops1/115156.shtml
And in LockedLinks in index 3 :
http://rotter.net/cgi-bin/forum/dcboard.cgi?az=read_count&om=115156&forum=scoops1
In both items there is the same number: 115156
Since this number exist in Links in index 32 and also in LockedLinks in index 3 then i want to remove this indexs from Links and LockedLinks.
In Links remove index 32 and in LockedLinks index 3.
I used substring to get the number from each List each itertion but it's never get inside never get to the string identical.
How can i make the comparison to work with the loops ? And how to make the remove of both indexs if identical ?
I would use Linq to remove duplicates:
using System.Collections.Generic;
using System.Linq;
...
List<int> distinct = list.Distinct().ToList();
http://www.dotnetperls.com/remove-duplicates-list
It's because this line:
if (Links[i].Contains(LockedLinks[x]))
is likely always evaluating to false. I'm not sure why you have that if statement.
Also to remove the items at those indexes you could keep a list if indexes for each array and add the value of x and i to each of them respectively when you find a match in the loop and then use those indexes to remove the items from each array after.
in your example this line would prevent the number comparison:
if (Links[i].Contains(LockedLinks[x]))
{
I think you should simply remove it.
First use more correct methods to get the numbers:
var num1 = Path.GetFileNameWithoutExtension("http://rotter.net/forum/scoops1/115156.shtml"); //return 115156
var num2 = HttpUtility.ParseQueryString(new Uri("http://rotter.net/cgi-bin/forum/dcboard.cgi?az=read_count&om=115156&forum=scoops1").Query)["om"]; //return 115156
Using these methods you can get two Lists, and then check for common numbers to remove them
var newList = list1nums.Except(list2nums).ToList();