2

I'm trying to sort a list based on the price for each item in the list.

Here's what I want my output to look like:

            ROLLS_ROYCE1 -- 6.608 €
            ROLLS_ROYCE3 -- 4.956 €
            ROLLS_ROYCE2 -- 0.826 €

However, here's what the current output actually is:

            ROLLS_ROYCE1 -- 6.608 €
            ROLLS_ROYCE2 -- 0.82 €
            ROLLS_ROYCE3 -- 4.956 €

Here's my code:

public void MyFunction() 
{
   List<string> mylist = new List<string>(new string[]
   {
      "ROLLS_ROYCE1 -- 0,826 € -- 8 PCS -- 14:02:53.876",
      "ROLLS_ROYCE2 -- 0,826 € -- 1 PCS -- 17:02:53.888",
      "ROLLS_ROYCE3 -- 0,826 € -- 6 PCS -- 18:09:55.888"
   });

   foreach (string f in mylist)
   {
      decimal b = Convert.ToDecimal(GetPrice(f), CultureInfo.GetCultureInfo("de-DE")) * Convert.ToDecimal(GetPieces(f));
      tradesforbigbuyslist += GetName(f) + " -- " + b.ToString() + " €" + 
         Environment.NewLine;
   }

   string[] splittedt2 = tradesforbigbuyslist.Split(new string[] { 
   System.Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            
   listBox3.DataSource = splittedt2;
}

public string GetPrice (string sourceline)
{
   string newstring = sourceline;
   string test1 = newstring.Replace(FetchThemAll.SubstringExtensions.Before(newstring, "--"), "");
   string textIWant = test1.Replace("--", "");
   string finalPrice = FetchThemAll.SubstringExtensions.Before(textIWant, "€");


   return finalPrice;
}

public string GetPieces(string sourceline)
{
   string ertzu = sourceline;
   string ertzu1 = FetchThemAll.SubstringExtensions.Between(ertzu, "€", "PCS");
   string ertzu2 = ertzu1.Replace("--", "");

   return ertzu2;
}

public string GetName(string sourceline)
{
   string barno = FetchThemAll.SubstringExtensions.Before(sourceline, "--");

   return barno;
}

How can I sort these strings correctly?

Donut
  • 110,061
  • 20
  • 134
  • 146
  • I cleaned up the formatting of your code a little bit. It looked like you were missing the start of your code (there was an unmatched closing bracket `}` on the line after `listBox3.DataSource = splittedt2;`, implying that it was the end of a method, but the start of the method was not in your original question). So, I put it inside a function called `MyFunction` - but please feel free to change it to your actual method name. – Donut Oct 29 '20 at 16:44
  • thx a lot buddy @Donut – Hassan Bel Haq Oct 29 '20 at 16:45
  • 8
    Why not try creating a class that stores these values as properties and represent each line as an object. Then a simple linq query could do this and also generate the output. – Omar Abdel Bari Oct 29 '20 at 16:45
  • To help you fix your code, we probably need to see `SubstringExtensions` -- i.e., a [mcve]. However, can't you just split the `"ROLLS_ROYCE1 -- 0,826 € -- 8 PCS -- 14:02:53.876"` strings using the `" -- "` substring using [`string.Split(String, StringSplitOptions)`](https://learn.microsoft.com/en-us/dotnet/api/system.string.split?view=netcore-3.1#System_String_Split_System_String_System_StringSplitOptions_)? See: [Split a string by another string in C#](https://stackoverflow.com/q/2245442/3744182). You can also split the `0,826 €` and `8 PCS` substrings using the same approach. – dbc Oct 29 '20 at 16:50
  • Agreed about creating a class, since you're already parsing the string anyway, may as well parse it into a type with a `string Name`, `decimal Price`, `int Quantity`, and `DateTime OrderTime` (or `TimeSpan`? not sure what the last field is). – Rufus L Oct 29 '20 at 16:55
  • I tried that by doing OrderByDescending but didnt work, actually my code works, its just that feature which i need barely, its just about sorting it from now here – Hassan Bel Haq Oct 29 '20 at 16:56
  • 1
    *I tried that by doing OrderByDescending but didnt work* -- Then can you please [edit] your code to share a [mcve] -- specifically the code that didn't work? – dbc Oct 29 '20 at 17:27

4 Answers4

2

You could simplify a lot of this work by representing each line of input as a class with relevant properties like this. If accuracy is super important like with dealing real money then fixed precision data type should represent the price. However I am using double below for simplicity.

public class Car {
    public string Name;
    public short Pieces;
    public double Price;
}

Then you would parse them at the beginning and have a list of these Car items. Assuming the Price above represents the desired value you wish to sort by the list you seek would be obtained by the following linq query.

var cars = new List<Cars>(); //Assumed definition
var frenchCulture = CultureInfo.CreateSpecificCulture("fr-FR"); //For Euros symbol usage later
//Parse Logic in Between
var sortedCars = cars.OrderByDescending(c => c.Price); //Linq Query yielding IEnumerable. If you must have a list simply append toList()

Then your output might be set like this.

foreach (var car in sortedCars)
    // output with string.format("{0} -- {1}", car.Name, car.Price.ToString("C3", frenchCulture))

Warning that this code was not tested but should be approximately correct. I did do some research for the string format.

Omar Abdel Bari
  • 934
  • 9
  • 17
0

Well, the format of your strings in mylist looks consistent enough that something like this might work (without using extension methods or Regex at all):

var parsed = mylist.Select(line => line.Split(new[] { " -- " }, StringSplitOptions.None)).Select(parts => new
{
   Name = parts[0],
   Price = Convert.ToDecimal(parts[1].Substring(0, parts[1].IndexOf(' '))),
   Pieces = Convert.ToInt32(parts[2].Substring(0, parts[2].IndexOf(' ')))
});

var sorted = parsed.OrderByDescending(x => x.Price * x.Pieces);

Then you can do whatever you want with sorted - e.g. convert the items back to strings and display them in listBox3.

Donut
  • 110,061
  • 20
  • 134
  • 146
  • Thanks a lot for your answer, unfortunately I cant compile, following error ` var parsed = mylist.Select(line => line.Split(" -- ")).Select(parts => new `Cannot convert string into char ` @donut – Hassan Bel Haq Oct 29 '20 at 17:08
  • @HassanBelHaq try `line.Split(new [] { " -- " }, StringSplitOptions.None)` – Donut Oct 29 '20 at 17:15
0

Here is what I did: I have tested this and it seems to work.

    public void MyFunction() 
{
   List<string> mylist = new List<string>(new string[]
   {
      "ROLLS_ROYCE1 -- 0,826 € -- 8 PCS -- 14:02:53.876",
      "ROLLS_ROYCE2 -- 0,826 € -- 1 PCS -- 17:02:53.888",
      "ROLLS_ROYCE3 -- 0,826 € -- 6 PCS -- 18:09:55.888"
   });
   
   var map = new Dictionary<string, double>();
   foreach (string f in mylist)
   {
      var inputs = f.Split(" -- "); //Creates a list of strings

      var unitPrice = Convert.ToDouble(inputs[1].Split(' ')[0]);
      var numUnits = Convert.ToDouble(inputs[2].Split(' ')[0]);
       
      var key = inputs[0];
      if(map.ContainsKey(key)) map[key] = numUnits*unitPrice;
      else map.Add(key, numUnits*unitPrice);      
   }

   var sortedMap = map.OrderByDescending(x=>x.Value);

   foreach(var item in sortedMap){
     Console.WriteLine($"{item.Key} -- {item.Value} €");
   }
}
mehtarit
  • 101
  • 1
  • 4
0

It may be overkill for you, but what I usually do in cases like this is create a class that knows how to parse one of those lines into strongly typed properties, like Name, Price, Quantity, etc. Usually I create a static method named Price that takes in an input string and returns an instance of the class.

In this case it would look something like:

public class Item
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int Quantity { get; set; }
    public TimeSpan Time { get; set; }
    public decimal Total => Price * Quantity;

    public static Item Parse(string input)
    {
        if (input==null) throw new ArgumentNullException();

        var parts = input
            .Split(new[] {"--", " ", "€", "PCS"}, 
                StringSplitOptions.RemoveEmptyEntries);

        if (parts.Length != 4)
            throw new ArgumentException(
                "Input must contain 4 sections separated by \"--\"");

        decimal price;
        if (!decimal.TryParse(parts[1], out price))
            throw new ArgumentException(
                "Price must be a valid decimal in the second position");

        int quantity;
        if (!int.TryParse(parts[2], out quantity))
            throw new ArgumentException(
                "Quantity must be a valid integer in the third position");

        TimeSpan time;
        if (!TimeSpan.TryParse(parts[3], out time))
            throw new ArgumentException(
                "Time must be a valid TimeSpan in the fourth position");

        return new Item
        {
            Name = parts[0],
            Price = price,
            Quantity = quantity,
            Time = time
        };
    }
}

With the work being done in the class, our main code is simplified tremendously:

List<string> mylist = new List<string>(new string[]
{
    "ROLLS_ROYCE1 -- 0,826 € -- 8 PCS -- 14:02:53.876",
    "ROLLS_ROYCE2 -- 0,826 € -- 1 PCS -- 17:02:53.888",
    "ROLLS_ROYCE3 -- 0,826 € -- 6 PCS -- 18:09:55.888"
});

List<Item> orderedItems = mylist
    .Select(Item.Parse)
    .OrderByDescending(item => item.Total)
    .ToList();

And then displaying the items would be as simple as:

orderedItems.ForEach(item => Console.WriteLine($"{item.Name} -- {item.Total} €"));

Output

enter image description here

Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • works fine only the calculated prices arent formated properly for me ( using net 4.5) , do I have to use CultureInfo or something similiar? @RufusL – Hassan Bel Haq Oct 29 '20 at 17:36
  • In what way are they not formulated properly? I had to change the commas to periods in the original strings since periods are the decimal separator in the US. What are you seeing in your culture? – Rufus L Oct 29 '20 at 17:53
  • I am seeing only numbers for example 6608 or 4956, I am from Germany – Hassan Bel Haq Oct 29 '20 at 17:56
  • Are those the correct numbers and you just want to add the thousands separator? Or are they not the correct numbers at all? You might try formatting them as currency: `$"{kvp.Key}={kvp.Value:C}"` – Rufus L Oct 30 '20 at 16:39