How to get a direction on the same train line? - c#

Can you help me with step by step logic that I need to get a direction on the same train line. with already having common train line with functions Next and Previous.
public IStation Next(IStation s)
{
if (!_stations.Contains(s))
{
throw new ArgumentException();
}
var index = _stations.IndexOf(s);
var isLast = index == _stations.Count -1;
if (isLast)
{
return null;
}
return _stations[index + 1];
}
public IStation Previous(IStation s)
{
if (!_stations.Contains(s))
{
throw new ArgumentException();
}
var index = _stations.IndexOf(s);
var isFirst = index == 0;
if (isFirst)
{
return null;
}
return _stations[index - 1];
}
And my function where I look for direction.
public string GetLineDirectiom(Station from, Station to, Line commonLine)
{
bool fromNextTo = true;
//to.Lines.Count();
//to.ToString();
var Final = commonLine.Next(from);
while (Final != null)
{
}
if (fromNextTo)
return "next";
else return "previous";
}

It looks like you are trying to "visit the stations along commonLine", starting at the from station.
The loop you have started is a valid start to that end; you need a variable to store the station you are currently visiting. Maybe the current variable name Final is a bit confusing to yourself here, because it is not the "final" station of the line, just the one you are currently visiting.
Therefore, let's name the variable currentStation. Then, you want to go to the next station until you have found to (and thereby know the direction), or until you have reached the end of the line:
var currentStation = from;
while (currentStation != null)
{
if (currentStation == to)
{
return "next";
}
currentStation = commonLine.Next(currentStation);
}
Now, this checks whether to is "ahead". If it wasn't, you can proceed to checking whether it can be found in the other direction, again starting at from:
currentStation = from;
while (currentStation != null)
{
if (currentStation == to)
{
return "previous";
}
currentStation = commonLine.Previous(currentStation);
}
If this loop doesn't find to either, apparently to is not on the line. Treat this case according to your preference.
Some remarks:
Indicating the direction as "next" or "previous" may be a bit misleading. If it really is the direction of the line, think about something like "forward" and "backward", as "next" and "previous" indeed imply the direct next/previous elements in a list.
While the above works, I do note that your Line object already has the stations in an indexed list. Therefore, a simpler way of achieving your goal might be to just determine the indices of the from and to stations on commonLine and compare which one is greater than the other.

It's not clear what you want to do and why you are returning string "next" and "prev" as a direction but in general to get the direction by the two stations:
public int GetStationIndex(IStation s)
{
var index = _stations.IndexOf(s);
if (index == -1)
{
throw new ArgumentException();
}
return index ;
}
public string GetLineDirection(Station from, Station to, Line commonLine)
{
var direction = commonLine.GetStationIndex(from)<commonLine.GetStationIndex(to)?"next" : "previous"
return direction;
}

Related

Make a method that contains a linear search?

I'm new to programmning and I'm taking a course to learn the basics in c#.
Right now I'm doing a console application that are supposed to work as a blog. In the application the user should be able to write a new post, show written posts and search for written posts. The application is supposed to be a list that contains arrays.
I'm almost finished with the application but I want to make a method for the linear search that searches for the written blogposts but I cant get it to work.
Here's the code for the linear search:
case 3:
Console.Write("Write the title for the post you are searching for: ");
string searchedWord = Console.ReadLine();
bool search = false;
for (int i = 0; i < myBlog.Count; i++)
{
if (searchedWord.ToUpper() == myBlog[i][0].ToUpper())
{
search = true;
Console.WriteLine("\nHe post you are looking for exists in the blog:");
Console.WriteLine("\nTitle: " + myBlog[i][0] +
"\nPost: " + myBlog[i][1] +
"\n\nPress enter to return to the menu...");
}
}
if (search == false)
{
Console.WriteLine("The searched word wasn't found. Press enter to return to the menu...");
}
break;
I made a try creating a method for it but I'm doing wrong, can somebody please tell me how to do it?
static string BlogSearch(List<string[]> myBlog, string searchedWord)
{
for (int i = 0; i < myBlog; i++)
{
if (searchedWord.ToUpper() == myBlog[i][0].ToUpper())
return i;
}
return -1;
}
If you are allowed to use Linq, you can do
using System.Linq;
//....
static string[] BlogSearch(List<string[]> myBlog, string searchedWord)
{
// "give me from myBlog ...
// the first element, that fits the criteria OR
// default if such an element is not in the list"
return myBlog.FirstOrDefault(x => x[0].Contains(searchedWord, StringComparison.OrdinalIgnoreCase));
}
See it in action in a Fiddle
Mind that this returns default(string) (which is null) if the searchedWord is not found.
I guess you are using string[] because your class (pun intended) has not come across the concept of classes, yet. So I won't go into that. Just so much: usually, you would model your blog data into a class with specific properties. And later on, you would probably want to keep the data in a Database instead of memory ... but all that is not really related to the problem at hand.
If you are NOT allowed to use Linq:
static string[] BlogSearch(List<string[]> myBlog, string searchedWord)
{
for( int i = 0; i < myBlog.Count; i++ )
{
if( myBlog[i][0].Contains(searchedWord, StringComparison.OrdinalIgnoreCase))
{
return myBlog[i];
}
}
return null;
}
Which is basically the same as the Linq version just coded out explicitly.
See it in action in a Fiddle.
Usage
// ... case 3: ...
var result = BlogSearch(myBlog, searchedWord);
if( result is null )
{
Console.WriteLine("The searched word wasn't found. Press enter to return to the menu...");
}
else
{
Console.WriteLine("\nThe post you are looking for exists in the blog:");
Console.WriteLine("\nTitle: " + result[0] +
"\nPost: " + result[1] +
"\n\nPress enter to return to the menu...");
}
break;
Some hints for you concerning your code:
// You expect to be returning `string`
// but all return statements return `int`.
// vv What you actually need is `string[]`, though.
static string BlogSearch(List<string[]> myBlog, string searchedWord)
{
// vv that's a `List<T>`, so you need `myBlog.Count` here
for (int i = 0; i < myBlog; i++)
{
// Two caveats:
// 1. _Exact_ match
// 2. `ToUpper` does not always work correctly.
// It is advised to use overloads with `StringComparison` argument
if (searchedWord.ToUpper() == myBlog[i][0].ToUpper())
return i;
}
return -1;
}

Implementing Custom Int+Range List Solution

I'm wondering if anyone can come up with a way to implement an array of numbers in a more memory efficient manner that will auto-organise itself into ranges. Example;
List testList = new List{1,2,3,4,5,6,7...};
vs
List<Range> testList = new List<Range>{1-3000,3002,4000-5000...};
Previously, I have asked a question just to confirm about whether or not this would in fact be a more memory efficient alternative. This question however pertains to actual application, how to implement this range list solution.
Index Array Storage Memory
I imagine this would perhaps need to be a custom list solution that would be a mix of ints and ranges. I'm picturing being able to .Add([int]) to the list, at which point it would determine if the value would cause a range to be added or to simply add the int value to the list.
Example
RangeList rangeList = new RangeList{1, 4, 7-9};
rangeList.Add(2);
//rangeList -> 1-2, 4, 7-9
rangeList.Add(3);
//rangeList -> 1-3, 4, 7-9
Details specific to my implementation
In my particular case, I'm analysing a very large document, line by line. Lines that meet a certain criteria need to be identified and then the overall list of line indexes need to be presented to the user.
Obviously displaying "Lines 33-32019 identified" is preferable to "Lines 33,34,35...etc". For this case, numbers will always be positive.
The first thing I would do is make a class which represents your range. You can provide some convenience like formatting as a string, and having an implicit cast from an int (This helps later implementation of the range list)
public class Range
{
public int Start{get; private set;}
public int End{get; private set;}
public Range(int startEnd) : this(startEnd,startEnd)
{
}
public Range(int start, int end)
{
this.Start = start;
this.End = end;
}
public static implicit operator Range(int i)
{
return new Range(i);
}
public override string ToString()
{
if(Start == End)
return Start.ToString();
return String.Format("{0}-{1}",Start,End);
}
}
You can then begin a simple implementation of the RangeList. By providing an Add method you can use a list initializer similar to List<T>:
public class RangeList : IEnumerable<Range>
{
private List<Range> ranges = new List<Range>();
public void Add(Range range)
{
this.ranges.Add(range);
}
public IEnumerator<Range> GetEnumerator()
{
return this.ranges.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator(){
return this.GetEnumerator();
}
}
At this point you can write some test code:
var rangeList = new RangeList(){
new Range(1,10),
15
};
foreach(var range in rangeList)
Console.WriteLine(range);
// Outputs:
// 1-10
// 15
Live example at this point: http://rextester.com/NCZSA71850
The next thing to do is provide an overload of Add which takes an int and finds the right range or adds a new one. A naive implemntation might look like the below (Assuming the addition of an Update method on range)
public void Add(int i)
{
// is it within or contiguous to an existing range
foreach(var range in ranges)
{
if(i>=range.Start && i<=range.End)
return; // already in a range
if(i == range.Start-1)
{
range.Update(i,range.End);
return;
}
if(i == range.End + 1)
{
range.Update(range.Start,i);
return;
}
}
// not in any ranges
ranges.Add(i);
}
Live example at this point: http://rextester.com/CHX64125
However this suffers from a few deficiencies
Does not merge ranges (say you already have 1-10 and 12-20 and you Add(11))
Does not re-order so if you have 1-5 and 20-25 and Add(7) this will be at the end not in the middle.
You can solve both problems by applying a sort after each addition, and some logic to determine if you should merge ranges
private void SortAndMerge()
{
ranges.Sort((a,b) => a.Start - b.Start);
var i = ranges.Count-1;
do
{
var start = ranges[i].Start;
var end = ranges[i-1].End;
if(end == start-1)
{
// merge and remove
ranges[i-1].Update(ranges[i-1].Start,ranges[i].End);
ranges.RemoveAt(i);
}
} while(i-- >1);
}
This needs to be called after every change to the list.
public void Add(Range range)
{
this.ranges.Add(range);
SortAndMerge();
}
public void Add(int value)
{
// is it within or contiguous to an existing range
foreach(var range in ranges)
{
if(value>=range.Start && value<=range.End)
return; // already in a range
if(value == range.Start-1)
{
range.Update(value,range.End);
SortAndMerge();
return;
}
if(value == range.End + 1)
{
range.Update(range.Start,value);
SortAndMerge();
return;
}
}
// not in any ranges
ranges.Add(value);
SortAndMerge();
}
Live example here: http://rextester.com/SYLARF47057
There are still some possible edge cases with this, which I urge you to work through.
UPDATE
The below will get this working as expected. This will merge up any added ranges/ints as you would expect and returns them correctly sorted. I've only changed the Add(Range) method, I think this is a fairly clean way of doing this.
public void Add(Range rangeToAdd)
{
var mergableRange = new List<Range>();
foreach (var range in ranges)
{
if (rangeToAdd.Start == range.Start && rangeToAdd.End == range.End)
return; // already exists
if (mergableRange.Any())
{
if (rangeToAdd.End >= range.Start - 1)
{
mergableRange.Add(range);
continue;
}
}
else
{
if (rangeToAdd.Start >= range.Start - 1
&& rangeToAdd.Start <= range.End + 1)
{
mergableRange.Add(range);
continue;
}
if (range.Start >= rangeToAdd.Start
&& range.End <= rangeToAdd.End)
{
mergableRange.Add(range);
continue;
}
}
}
if (!mergableRange.Any()) //Standalone range
{
ranges.Add(rangeToAdd);
}
else //merge overlapping ranges
{
mergableRange.Add(rangeToAdd);
var min = mergableRange.Min(x => x.Start);
var max = mergableRange.Max(x => x.End);
foreach (var range in mergableRange) ranges.Remove(range);
ranges.Add(new Range(min, max));
}
SortAndMerge();
}
Finally, we need if (ranges.Count > 1) in the SortAndMerge() method to prevent an index error when the first range is added.
And with that, I think this fully satisfies my question.

TopCoder Alogirithm System Test Failure

I am doing a problem from Top-Coder.The problem statement is-
One day, Jamie noticed that many English words only use the letters A
and B. Examples of such words include "AB" (short for abdominal),
"BAA" (the noise a sheep makes), "AA" (a type of lava), and "ABBA" (a
Swedish pop sensation).
Inspired by this observation, Jamie created a simple game. You are
given two Strings: initial and target. The goal of the game is to find
a sequence of valid moves that will change initial into target. There
are two types of valid moves:
Add the letter A to the end of the string. Reverse the string and then
add the letter B to the end of the string. Return "Possible" (quotes
for clarity) if there is a sequence of valid moves that will change
initial into target. Otherwise, return "Impossible".
Below is my solution of the problem which passed all the tests in the Panel but failed in system test.But,I did not get any specific information about which test case failed.Please check if my code will not work in some scenario.
class ABBA
{
public string canObtain(string initial,string target)
{
string s = "Impossible";
if (initial.Length > target.Length)
return "Impossible";
if (initial.Equals(target))
return "Possible";
if (CheckFirstWay(target,initial))
{
s=canObtain(initial+"A",target);
}
if (s.Equals("Impossible") && CheckSecondWay(target,initial))
{
s=canObtain(ReverseStringDirect(initial) + "B",target);
}
return s;
}
public static string ReverseStringDirect(string s)
{
char[] array = new char[s.Length];
int forward = 0;
for (int i = s.Length - 1; i >= 0; i--)
{
array[forward++] = s[i];
}
return new string(array);
}
private static bool CheckSecondWay(string final, string initial)
{
if (final.Contains(ReverseStringDirect(initial) + "B") || final.Contains("B"+initial))
{
return true;
}
else
{
return false;
}
}
private static bool CheckFirstWay(string final1, string initial)
{
if (final1.Contains(initial + "A") || final1.Contains(ReverseStringDirect(initial+"A")))
{
return true;
}
else
{
return false;
}
}
}
You can check on which test failed by following procedure,
Go to the specified room.
Open the problem.
Compile.
Submit it.
Then go to run system test.
you will see the error test there.
OR
Select the match from here.
Then in the page as shown you will see set of blue coders and red coders who topped the match.
Select any one player as per your division. [.] --> this logo beside name.
Then check their solution you will see those tests.
Here are the test cases..check it out.
You have to type system tests here. You can check the image below. Image credit : Google

How would I return the lowest value from the list without using shortcuts?

I was given a list of numbers, { 1, 2, 3, 4, 5, 6, 7 }. I was asked to return the lowest value without using shortcuts, e.g. .Min() etc.
I am not going to give you the answer to your homework question for you directly, however to get you started just loop over the list and keep track of the smallest one you found. When you are done the smallest you found is the smallest in the list.
For your second part, it is just basic problem solving. Look at the problem, break it into smaller pieces. For example, for your problem you could break it in to:
How do I loop over a list
How do I remember a value between loops
How do i compare if the remembered value is smaller than the current loop value
How do I replace my remembered value if the current loop value is smaller
Then solve each piece individually.
You can do this in old-fashion imperative way. Works with all comparable types:
public static class MyEnumerableExtensions
{
public static T Min<T>(this IEnumerable<T> list) where T : IComparable<T>
{
if (list == null)
{
throw new ArgumentNullException("list");
}
T min = default (T);
bool initialized = false;
foreach (T elem in list)
{
if (!initialized)
{
min = elem;
initialized = true;
}
else if (min == null) // Do not compare with null, reset min
{
min = elem;
}
else if (elem != null && min.CompareTo(elem) > 0) // Compare only when elem is not null
{
min = elem;
}
}
if (!initialized)
{
throw new InvalidOperationException("list is empty");
}
return min;
}
}
Usage:
var min1 = list.Min();
var min2 = MyEnumerableExtensions.Min(list);
Also, is only Min method is restricted from Linq, additional tricks are possible. Works for numbers only:
var minViaMax = -list.Select(x => -x).Max();
var minViaAggregate = list.Aggregate(Math.Min);

ArgumentNullException in an array that I can't figure out

I have been up half the night and still trying to get this null exception figured out. I have read a few of texts about this issue but none has helped me in any way, to me what the problem is as it should work :/ It just crashes at this piece of code:
Private void UpdateGUI()
{
string selectedItem = cmbDisplayOptions.Items[cmbDisplayOptions.SelectedIndex].ToString();
rdbtReserv.Checked = true;
lstReservations.Items.Clear();
lstReservations.Items.AddRange(m_seatMngr.GetSeatInfoStrings(selectedItem));
}
lstReservations.Items.AddRange(m_seatMngr.GetSeatInfoStrings(selectedItem)); Gives me the ArgumentNullExeption, but to me it should not do that.
the addrange sends string selectedItem to another class:
public string[] GetSeatInfoStrings(string selectedItem)
{
int count = GetNumOfSeats(selectedItem);
if (count <= 0)
{
return null;
}
string[] strSeatInfoStrings = new string[count];
for (int index = 0; index <= m_totNumOfSeats - 1; index++)
{
strSeatInfoStrings[index] = GetSeatInfoAt(index);
}
return strSeatInfoStrings;
}
This int count = GetNumOfSeats(selectedItem); goes to here and returns with an int:
private int GetNumOfSeats(string selectedItem)
{
if (selectedItem == "ReservedSeats")
{
return GetNumReserved();
}
if (selectedItem == "VacantSeats")
{
return GetNumVacant();
}
else
{
return m_totNumOfSeats;
}
}
I have checked the arrayed have the correct number of spaces(60) and that selectedItem has a string(Allseats to start with so it should return m_totnumOfSeats which is an int of 60) But then in the private int GetNumOfSeats something goes wrong and it returns null and...well why?
I can't see the problem.. maybe gone blind by trying to find the issue. Always got outstanding help here and I have learned tons!! So maybe someone can point out all the issues there is in my code.
Thanks a million in advance for any and all advice!
//Regards
Check if your variables are actually initialized and returns correct values.
There are logical errors in the GetSeatInfoStrings methods and the GetNumofSeats method.
Lucky for you the GetNumOfSeats method will always return 60 for you because of the wrong way you compare strings. It's not the right way, so use the Equals method for comparison like
if (selectedItem.Equals("ReservedSeats"))
With that you will get a proper output form GetNumOfSeats(string) method.
The next thing is to fix your looping in the GetSeatInfoStrings method so as to not get an array index out of bounds exception like this.
string[] strSeatInfoStrings = new string[count];
for (int index = 0; index <= count; index++)
{
strSeatInfoStrings[index] = GetSeatInfoAt(index);
}
return strSeatInfoStrings;
Also fix the part where your logic returns a null in the GetSeatInfoStrings method. it should return an empty string array according to your logic as
return new string[0];
That should probably get your methods working. You need to be very careful of what you code before you debug it :-)
Looking at the source code of ObjectCollection, when you call AddRange and pass a null value, you get back the ArgumentNullException.
You could prevent this changing this code
if (count <= 0)
{
return new string[0];
}

Categories