0

1.txt:

Origination,destination,datetime,price

YYZ,YTC,2016-04-01 12:30,$550
YYZ,YTC,2016-04-01 12:30,$550
LKC,LKP,2016-04-01 12:30,$550

2.txt:

Origination|destination|datetime|price

YYZ|YTC|2016-04-01 12:30|$550
AMV|YRk|2016-06-01 12:30|$630
LKC|LKP|2016-12-01 12:30|$990

I have two text files with ',' and '|' as separators, and I want to create a console app in C# which reads these two files when I pass an origination and destination location from command prompt.

While searching, I want to ignore duplicate lines, and I want to display the results in order by price.

The output should be { origination } -> { destination } -> datetime -> price

Need help how to perform.

CarenRose
  • 1,266
  • 1
  • 12
  • 24
cshah
  • 13
  • 5
  • 6
    Do you have a code sample of what you've got so far? – Gareth Jun 22 '17 at 14:12
  • 1
    Do you know how to read text files? Also, if you're creating a console app why have you tagged it with `asp.net`? – Matt Hogan-Jones Jun 22 '17 at 14:14
  • 4
    Learn how to read files line by line. Learn how to split strings by a given separator: ',' or '|' etc. Learn how to compare strings. Learn how to concatenate strings and variables using string interpolation using $ dollar sign and {} brackets for the variables. Read both files line by line. Split and Compare the strings if they don't match append a Collection with both or only one of them depending on if they match or not constructing the new strings you want using string interpolation. If you already know that then show us some code samples so we can help you improving them – Kalin Krastev Jun 22 '17 at 14:29
  • while i am reading path of txt files which is in side my proj using below System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase); its giving me wrong path its showing debug folder path my files are not here file:\c:\users\502703944\documents\visual studio 2013\Projects\searchTest\searchTest\bin\Debug – cshah Jun 22 '17 at 15:40

2 Answers2

1

I'm not 100% clear on what the output of your program is supposed to be, so I'll leave that part of the implementation up to you. My strategy was to use a constructor method that takes a string (that you will read from a file) and a delimiter (since it varies) and use that to create objects which you can manipulate (e.g. add to hash sets, etc).

PriceObject.cs

using System;
using System.Globalization;

namespace ConsoleApplication1
{
class PriceObject
{
    public string origination { get; set; }
    public string destination { get; set; }
    public DateTime time { get; set; }
    public decimal price { get; set; }



    public PriceObject(string inputLine, char delimiter)
    {
        string[] parsed = inputLine.Split(new char[] { delimiter }, 4);
        origination = parsed[0];
        destination = parsed[1];
        time = DateTime.ParseExact(parsed[2], "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
        price = Decimal.Parse(parsed[3], NumberStyles.Currency, new CultureInfo("en-US"));
    }


    public override bool Equals(object obj)
    {
        var item = obj as PriceObject;
        return origination.Equals(item.origination) &&
            destination.Equals(item.destination) &&
            time.Equals(item.time) &&
            price.Equals(item.price);
    }

    public override int GetHashCode()
    {

        unchecked
        {
            var result = 17;
            result = (result * 23) + origination.GetHashCode();
            result = (result * 23) + destination.GetHashCode();
            result = (result * 23) + time.GetHashCode();
            result = (result * 23) + price.GetHashCode();
            return result;
        }
    }


}
}

Program.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        HashSet<PriceObject> list1 = new HashSet<PriceObject>();
        HashSet<PriceObject> list2 = new HashSet<PriceObject>();

        using (StreamReader reader = File.OpenText(args[0]))
        {
            string line = reader.ReadLine(); // this will remove the header row

            while (!reader.EndOfStream)
            {
                line = reader.ReadLine();
                if (String.IsNullOrEmpty(line))
                    continue;
                // add each line to our list
                list1.Add(new PriceObject(line, ','));
            }

        }

        using (StreamReader reader = File.OpenText(args[1]))
        {
            string line = reader.ReadLine(); // this will remove the header row

            while (!reader.EndOfStream)
            {
                line = reader.ReadLine();
                if (String.IsNullOrEmpty(line))
                    continue;
                // add each line to our list
                list2.Add(new PriceObject(line, '|'));
            }

        }

        // merge the two hash sets, order by price
        list1.UnionWith(list2);
        List<PriceObject> output = list1.ToList();

        output.OrderByDescending(x => x.price).ToList();

        // display output here, e.g. define your own ToString method, etc
        foreach (var item in output)
        {
            Console.WriteLine(item.ToString());
        }

        Console.ReadLine();
    }
}
}
C. Helling
  • 1,394
  • 6
  • 20
  • 34
  • Just a note, since according to your comments you are having trouble loading the files: I ran the above program with the command line arguments: C:\temp\test1.txt C:\temp\test2.txt – C. Helling Jun 22 '17 at 16:03
  • Thanks let me try one more question on command prompt user will add '$search -o YYZ -d YYC' and based on this i need to validate whole process and need to display result how to handle this? also in my txt file i have static header above data how to skip parsing that headers? thanks in advance – cshah Jun 22 '17 at 16:49
  • Not sure what you mean by "validate whole process," but if you're trying to find the results that match e.g. origination "YYZ", destination "YTC", you can take those parameters as variables into the linq query. E.g. `PriceObject outputObject = output.Where(x => x.origination.Equals("YYZ") && x.destination.Equals("YTC")).FirstOrDefault();`. To skip headers, I did this in the beginning: the `reader.ReadLine()` before the while-loop will peel off the headers. I will edit a comment to make this clearer. – C. Helling Jun 22 '17 at 16:57
  • Are you familiar with running a console application from the command line? Do they really need to type "$search" etc? See here: https://stackoverflow.com/questions/12998415/calling-a-console-app-from-the-command-line-with-string-args If I were you, I'd just store the origination and destination as variables, e.g. `string origination = args[2]` or whatever it is. – C. Helling Jun 22 '17 at 17:31
1

Here's a simple solution that works for your example files. It doesn't have any error checking for if the file is in a bad format.

using System;
using System.Collections.Generic;

class Program
{
    class entry
    {
        public string origin;
        public string destination;
        public DateTime time;
        public double price;
    }

    static void Main(string[] args)
    {
        List<entry> data = new List<entry>();

        //parse the input files and add the data to a list
        ParseFile(data, args[0], ',');
        ParseFile(data, args[1], '|');

        //sort the list (by price first)
        data.Sort((a, b) =>
        {
            if (a.price != b.price)
                return a.price > b.price ? 1 : -1;
            else if (a.origin != b.origin)
                return string.Compare(a.origin, b.origin);
            else if (a.destination != b.destination)
                return string.Compare(a.destination, b.destination);
            else
                return DateTime.Compare(a.time, b.time);
        });

        //remove duplicates (list must be sorted for this to work)
        int i = 1;
        while (i < data.Count)
        {
            if (data[i].origin == data[i - 1].origin
                && data[i].destination == data[i - 1].destination
                && data[i].time == data[i - 1].time
                && data[i].price == data[i - 1].price)
                data.RemoveAt(i);
            else
                i++;
        }

        //print the results
        for (i = 0; i < data.Count; i++)
            Console.WriteLine("{0}->{1}->{2:yyyy-MM-dd HH:mm}->${3}",
                data[i].origin, data[i].destination, data[i].time, data[i].price);

        Console.ReadLine();
    }

    private static void ParseFile(List<entry> data, string filename, char separator)
    {
        using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open))
        using (System.IO.StreamReader reader = new System.IO.StreamReader(fs))
            while (!reader.EndOfStream)
            {
                string[] line = reader.ReadLine().Split(separator);
                if (line.Length == 4)
                {
                    entry newitem = new entry();
                    newitem.origin = line[0];
                    newitem.destination = line[1];
                    newitem.time = DateTime.Parse(line[2]);
                    newitem.price = double.Parse(line[3].Substring(line[3].IndexOf('$') + 1));
                    data.Add(newitem);
                }
            }
    }
}
Tobbs
  • 1,120
  • 1
  • 6
  • 15
  • Thanks let me try one more question on command prompt user will add '$search -o YYZ -d YYC' and based on this i need to validate whole process and need to display result how to handle this? also in my txt file i have static header above data how to skip parsing that headers? thanks in advance – cshah Jun 22 '17 at 16:49
  • i got one thing how to skip - reader.ReadLine().Skip(1); but other command promt this is still pending – cshah Jun 22 '17 at 16:55