I want to write a string with some variables but when the specific value isn't necessary, it can be cut off.
e.g.:
int apples = 5;
int oranges = 8
int bananas = 0;
string.Format("I got {0} apples, {1} oranges, {2} bananas.", apples, oranges, bananas)
Output: I got 5 apples, 8 oranges, 0 bananas.
I want to cut off ", 0 bananas", as they aren't necessary to show here.
My only solution would be if-states for every fruit... Imagine I can have 10 fruits...
if (bananas == 0)
{
string.Format("I got {0} apples, {1} oranges.", apples, oranges)
}
My next problem is that I can have more than one fruit to be 0. This will be an endless if-state within an if-state...
Is there any solution to solve this within one line?
I don't even know what I can do here. I just know the ways of inverting variables with the use of + operator, using string.Format() or using $ before the actual string.
Many thanks!
Netroshin
You don't have to have a bunch of different if statements for every possibly combination, you can just build the string with a single if for each fruit. This can be simplified using the ?: ternary operator, which has a condition on the left side of the ?, followed by a result if the condition is true, then a : followed by a result if the condition is false. Since we want a comma at the end of each string, I added a TrimEnd(',') to remove the last one:
string result = "I got" +
((apples > 0 ? $" {apples} apples," : "") +
(oranges > 0 ? $" {oranges} oranges," : "") +
(bananas > 0 ? $" {bananas} bananas" : "")).TrimEnd(',');
You also might consider putting your data into a better structure. Instead of storing them as int types, you could create your own type that has both a string name and an int quantity. In this way, you can filter a list of these items on the quantity (remove all where quantity == 0), and then print them out using the Name property.
For example:
public class Fruit
{
public string Name { get; set; }
public int Quantity { get; set; }
}
public class Program
{
// Create a list of fruits
List<Fruit> fruits = new List<Fruit>();
fruits.Add(new Fruit { Name = "Apples", Quantity = 5 });
fruits.Add(new Fruit { Name = "Oranges", Quantity = 8 });
fruits.Add(new Fruit { Name = "Bananas", Quantity = 0 });
string result = "I got " + string.Join(", ", fruits
.Where(fruit => fruit.Quantity > 0)
.Select(fruit => $"{fruit.Quantity} {fruit.Name}"));
Console.WriteLine(result);
Console.ReadKey();
}
How about
StringBuilder sb;
sb.Append("I got ");
if(oranges > 0){
sb.Append(String.Format("{0} oranges,"));
}
if(apples > 0){
sb.Append(String.Format("{0} apples,"));
}
...
var res = sb.ToString();
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp_StackoverflowAnswering1
{
class Fruit
{
public String Name;
public int Value;
public Fruit(String name, int value)
{
this.Name = name;
this.Value = value;
}
}
class Program
{
static void Main(string[] args)
{
List<Fruit> myFruits = new List<Fruit> { new Fruit("Banana", 0),
new Fruit("Apple", 5),
new Fruit("Orange", 10) };
String output = $"I got {String.Join(", ", myFruits.Where(fruit => fruit.Value > 0).Select(fruit => $"{fruit.Name}={fruit.Value}"))}.";
Console.WriteLine(output);
}
}
}
Related
I have an array list of List<string> that contains values in the following order ["1m", "1cm", "4km","2cm"] (Centimeters, meters and kilometers)
When I want to sort this array, I get a wrong answer. I use OrderBy:
List<string> data = new List<string> { "1m", "1cm", "4km","2cm" };
var result= data.OrderBy(x => x).ToList();
the result is:
{ "1cm", "1m", "2cm", "4km"}
But I want the answer to be this order-: { "1cm", "2cm", "1m", "4km"}
You have sorted the data alphabetically. First the first character is compared. Then the second character and...
You need to normalize the data based on cm(or m) and then sort.
List<string> data = new List<string> { "1m", "1cm", "4km","2cm" };
var result = data.OrderBy(x => lenghtCM(x));
public int lenghtCM(string lenghtStr)
{
if (lenghtStr.Contains("cm"))
{
string num = lenghtStr.Split("cm")[0];
return int.Parse(num);
}
else if (lenghtStr.Contains("km"))
{
string num = lenghtStr.Split("km")[0];
return int.Parse(num) * 100*1000;
}
else if (lenghtStr.Contains("m"))
{
string num = lenghtStr.Split('m')[0];
return int.Parse(num) * 100;
}
return 0;
}
then the result:
{ "1cm", "2cm", "1m", "4km"}
private string[] normalaizeArray(string[] inputArray)
{
for (int i= 0 ; i < inputArray.Length; i++)
{
if(inputArray[i].Contains('m'))
{
inputArray[i] = (float.Parse(inputArray[i].Split('k')[0]) * 100).ToString();
} else if(inputArray[i].Contains('km'))
{
inputArray[i] = (float.Parse(inputArray[i].Split('k')[0]) * 100*1000).ToString();
}
else
{
inputArray[i] = inputArray[i].Replace("cm", "");
}
}
inputArray = inputArray.OrderBy(x => int.Parse(x)).ToArray();
for (int i = 0; i < inputArray.Length; i++)
{
if(int.Parse(inputArray[i])>1000*100)
inputArray[i] = (float.Parse(inputArray[i])/1000).ToString() + "km";
else if(int.Parse(inputArray[i])>100)
inputArray[i] = (float.Parse(inputArray[i])/100).ToString() + "m";
else
inputArray[i] = inputArray[i] + 'cm';
}
return inputArray;
}
If you can, parse the strings first:
enum Unit { cm, m, km }
record Measurment(int Length, Unit Unit)
{
public override string ToString() => $"{Length}{Enum.GetName(typeof(Unit), Unit)}";
public double NormalizedLength => Unit switch
{
Unit.cm => Length * 0.001,
Unit.m => Length * 1.0,
Unit.km => Length * 1000.0,
_ => throw new NotImplementedException()
};
public static Measurment Parse(string source)
{
var digits = source.TakeWhile(char.IsDigit).Count();
var length = int.Parse(source.AsSpan(0, digits));
// switches with source.AsSpan(digits) in preview
var measure = source[..digits] switch
{
"cm" => Unit.cm,
"m" => Unit.m,
"km" => Unit.km,
_ => throw new NotImplementedException(),
};
return new Measurment(length, measure);
}
}
.
var result = data.Select(Measurment.Parse).OrderBy(x => x.NormalizedLength).ToList();
This lets you sort your measurments by NormalizedLength and ToString gets back the original string. Should be very fast, simple to extend with new units and you can make it fault-tolerant if you turn Parse into the TryParse pattern.
There's a NuGet package to manage parsing and manipulating SI units called UnitsNet.
If you install that package (via Add | NuGet Package, search for and select UnitsNet and install it), then you can write the following code:
(You'll need to add using UnitsNet; at the top of the code file first)
This also works with nm etc.
List<string> data = new List<string> { "1m", "1cm", "4km", "2cm" };
var result = data.OrderBy(Length.Parse).ToList();
Console.WriteLine(string.Join(", ", result));
This will output "1cm, 2cm, 1m, 4km"
You need custom sort using IComparable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication49
{
class Program
{
static void Main(string[] args)
{
List<string> data = new List<string> { "1m", "1cm", "4km", "2cm" };
List<string> results = data.Select(x => new SortDistance(x)).OrderBy(x => x).Select(x => x.value).ToList();
}
}
public class SortDistance : IComparable<SortDistance>
{
const string pattern = #"(?'number'\d+)(?'multiplier'.*)";
List<string> distanceOrder = new List<string>() { "cm", "m", "km" };
public string value { get; set; }
public int distance { get; set; }
public string multiplier { get; set; }
public SortDistance(string value)
{
this.value = value;
Match match = Regex.Match(value, pattern);
this.distance = int.Parse(match.Groups["number"].Value);
this.multiplier = match.Groups["multiplier"].Value;
}
public int CompareTo(SortDistance other)
{
if (this.multiplier == other.multiplier)
return this.distance.CompareTo(other.distance);
else
return distanceOrder.IndexOf(this.multiplier).CompareTo(distanceOrder.IndexOf(other.multiplier));
}
}
}
you can not sort using OrderBy.
You have to define the conversion first from all units to the smallest unit. for example m to cm, km to cm.....
so 1m euqals to 100 cm
then you have to iterate through your list and check each item's unit, get its equivalent to the smallest unit.
Create another list.
you can implement insertion sort to sort the items and add keep on inserting the item based on the comparison.
The idea of the program is to output the department which has the biggest salary combined by every person working in that department.
so I have my program.cs:
string print = string.Empty;
int n = int.Parse(Console.ReadLine());
for(int a = 0; a < n; a++)
{
string input = Console.ReadLine();
List<string> inputs = input.Split(" ").ToList();
if(inputs[4].Contains("#"))
{
Employee info = new Employee(inputs[0], double.Parse(inputs[1]), inputs[2], inputs[3], inputs[4], int.Parse(inputs[5]));
print = info.ToString();
}
else
{
Employee info = new Employee(inputs[0], double.Parse(inputs[1]), inputs[2], inputs[3], "n/a", int.Parse(inputs[4]));
print = info.ToString();
}
Employee.Calculation(inputs[3], double.Parse(inputs[1]));
}
Console.WriteLine(print);
and part of my Employee.cs, which is the inportant one:
public static void Calculation(string department, double salary)
{
Dictionary<string, double> data = new Dictionary<string, double>();
if (data.ContainsKey(department))
{
data[department] += salary;
}
else
{
data.Add(department, salary);
}
foreach (KeyValuePair<string, double> info in data)
{
if (info.Value > biggestSalary)
{
biggestSalary = info.Value;
toReturn = info.Key;
}
}
}
public override string ToString()
{
string line1 = "Highest average salary: " + toReturn;
return line1;
}
with this input:
4
Pesho 120000 Dev Daskalo pesho#abv.bg 28
Toncho 333333.33 Manager Marketing 33
Ivan 15000 ProjectLeader Development ivan#ivan.com 40
Gosho 130033333 Freeloader Nowhere 18
the last line is ignored for some reason when I debugged it and it returns the 2nd biggest salary - in department "Marketing".
with this input:
6
Stanimir 496.37 Temp Coding stancho#yahoo.com 50
Yovcho 610.13 Manager Sales 33
Toshko 609.99 Manager Sales toshko#abv.bg 44
Venci 0.02 Director BeerDrinking beer#beer.br 23
Andrei 700.00 Director Coding 45
Popeye 13.3333 Sailor SpinachGroup popeye#pop.ey 67
i get "Coding" instead of "Sales". When you combine the 2 people working "Coding" you get 700 + 496 = 1196. When you combine the 2 people working in "Sales" you get 609 + 610 = 1219 and then the output should be "Highest average salary: Sales", but instead the output is "Highest average salary: Coding";
You are creating a new dictionary every time the Calculation method is called.
Dictionary<string, double> data = new Dictionary<string, double>();
// The first block is never called as the Dictionary never contains anything at this point. The else block always runs.
if (data.ContainsKey(department))
{
data[department] += salary;
}
else
{
data.Add(department, salary);
}
There is therefore only ever one value in the dictionary for the one employee that that is meant to be added.
Since an employee with the Coding department has the highest individual value, that is the department that is returned.
Without commenting on the other aspects of the code the way to avoid this issue is to create the Dictionary first outside of the Calculation method.
Assuming that you would add Employee to the List<Employee>, finding this value using LINQ would look like
public class Employee
{
public string Name { get; set; }
public string Dept { get; set; }
public decimal Salary { get; set; }
}
// preload
var empList = new List<Employee>();
empList.Add(new Employee(){Name = "A", Salary = 10, Dept = "Sales" });
empList.Add(new Employee(){Name = "B", Salary = 10, Dept = "Coding" });
empList.Add(new Employee(){Name = "C", Salary = 30, Dept = "Sales" });
empList.Add(new Employee(){Name = "D", Salary = 20, Dept = "Coding" });
// execute
var topItem = empList.GroupBy(_ => _.Dept)
.Select(g => new { D = g.First().Dept, TS = g.Sum(s => s.Salary)})
.OrderByDescending(item => item.TS)
.First();
Console.WriteLine($"The department '{topItem.D}' has biggest salary: '{topItem.TS}'");
The department 'Sales' has biggest salary: '40'
Returning to the point that your code structure has a role in your problem. Even if you calculate this in the loop, you still want to accumulate your employees on the class/application scope variable, so you have continuous access to it.
As applicable to your case, your Employee info = new Employee seem not added to any list. And Employee.Calculation does not use any program-lever variable, which would keep the state.
If I wanted to keep this structure, the one you have, I could declare your class like
public class Employee
{
private static List<Employee> _empList = new List<Employee>();
// your constructor
public Employee (........)
{
// Assign your properties here
_empList.Add(this);
}
// And your `Employee.Calculation` would lose any parameters and look like this
public static void Calculation()
{
var topItem = _empList.GroupBy(_ => _.Dept).......
Console.WriteLine($"The department '{topItem.D}' has biggest salary: '{topItem.TS}'");
}
}
^^^ that is if I really wanted to fix the issue but keep the structure you have already
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?
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.
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.
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} €");
}
}
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
String Array 1: (In this format: <MENU>|<Not Served?>|<Alternate item served>)
Burger|True|Sandwich
Pizza|True|Hot Dog
String Array 2: (Contains Menu)
Burger
Pizza
Grill Chicken
Pasta
I need the menu is served or any alternate item served for that particular item.
Code:
for(int i = 0; i < strArr2.Length; i++)
{
if(strArr2.Any(_r => _r.Split('|').Any(_rS => _rS.Contains(strArr1[i]))))
{
var menu = strArr2[i];
var alternate = ? // need to get alternate item
}
}
As I commented in the code, how to get the alternate item in that string array? Please help, thanks in advance.
P.S: Any help to trim if condition is also gladly welcome.
Instead of any, you may use Where to get the value matching.
#Markus is having the detailed answer, I am just using your code to find a quick fix for you.
for(int i = 0; i < strArr2.Length; i++)
{
if(strArr2.Any(_r => _r.Split('|').Any(_rS => _rS.Contains(strArr1[i]))))
{
var menu = strArr2[i];
var alternate = strArr2.Where(_rs => _rs.Split('|').Any(_rS => _rS.Contains(strArr1[i]))).First().Split('|').Last();
}
}
In order to simplify your code, it is a good idea to better separate the tasks. For instance, it will be much easier to handle the contents of string array 1 after you have converted the contents into objects, e.g.
class NotServedMenu
{
public string Menu { get; set; }
public bool NotServed { get; set; }
public string AlternateMenu { get; set; }
}
Instead of having an array of strings, you can read the strings to a list first:
private IEnumerable<NotServedMenu> NotServedMenusFromStrings(IEnumerable<string> strings)
{
return (from x in strings select ParseNotServedMenuFromString(x)).ToArray();
}
private NotServedMenu ParseNotServedMenuFromString(string str)
{
var parts = str.Split('|');
// Validate
if (parts.Length != 3)
throw new ArgumentException(string.Format("Unable to parse \"{0}\" to an object of type {1}", str, typeof(NotServedMenu).FullName));
bool notServedVal;
if (!bool.TryParse(parts[1], out notServedVal))
throw new ArgumentException(string.Format("Unable to read bool value from \"{0}\" in string \"{1}\".", parts[1], str));
// Create object
return new NotServedMenu() { Menu = parts[0],
NotServed = notServedVal,
AlternateMenu = parts[2] };
}
Once you can use the objects, the subsequent code will be much cleaner to read:
var notServedMenusStr = new[]
{
"Burger|True|Sandwich",
"Pizza|True|Hot Dog"
};
var notServedMenus = NotServedMenusFromStrings(notServedMenusStr);
var menus = new[]
{
"Burger",
"Pizza",
"Grill Chicken",
"Pasta"
};
var alternateMenus = (from m in menus join n in notServedMenus on m equals n.Menu select n);
foreach(var m in alternateMenus)
Console.WriteLine("{0}, {1}, {2}", m.Menu, m.NotServed, m.AlternateMenu);
In this sample, I've used a Linq join to find the matching items.
You could do something like that
string[] strArr1 = { "Burger|True|Sandwich", "Pizza|True|Hot Dog" };
string[] strArr2 = { "Burger", "Pizza", "Grill Chicken", "Pasta" };
foreach (string str2 in strArr2)
{
string str1 = strArr1.FirstOrDefault(str => str.Contains(str2));
if (str1 != null)
{
string[] splited = str1.Split('|');
string first = splited[0];
bool condition = Convert.ToBoolean(splited[1]);
string second = splited[2];
}
}
I would like to remove the duplicate elements from a List. Some elements of the list looks like this:
Book 23
Book 22
Book 19
Notebook 22
Notebook 19
Pen 23
Pen 22
Pen 19
To get rid of duplicate elements i've done this:
List<String> nodup = dup.Distinct().ToList();
I would like to keep in the list just
Book 23
Notebook 22
Pen 23
How can i do that ?
you can do someting like
string firstElement = dup.Distinct().ToList().First();
and add it to another list if you want.
It's not 100% clear what you want here - however...
If you want to keep the "largest" number in the list, you could do:
List<string> noDup = dup.Select(s => s.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)
.Select(p => new { Name=p[0], Val=int.Parse(p[1]) })
.GroupBy(p => p.Name)
.Select(g => string.Join(" ", g.Key, g.Max().ToString()))
.ToList();
This would transform the List<string> by parsing the numeric portion into a number, taking the max per item, and creating the output string as you have specified.
You can use LINQ in combination with some String operations to group all your itemy by name and MAX(Number):
var q = from str in list
let Parts = str.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
let item = Parts[ 0 ]
let num = int.Parse(Parts[ 1 ])
group new { Name = item, Number = num } by item into Grp
select new {
Name = Grp.Key,
Value = Grp.Max(i => i.Number).ToString()
};
var highestGroups = q.Select(g =>
String.Format("{0} {1}", g.Name, g.Value)).ToList();
(Same as Reed's approach but in query syntax which is better readable to my mind)
Edit: I cannot reproduce your comment that it does not work, here is sample data:
List<String> list = new List<String>();
list.Add("Book 23");
list.Add("Book 22");
list.Add("Book 19");
list.Add("Notebook 23");
list.Add("Notebook 22");
list.Add("Notebook 19");
list.Add("Pen 23");
list.Add("Pen 22");
list.Add("Pen 19");
list.Add("sheet 3");
var q = from str in list
let Parts = str.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
let item = Parts[ 0 ]
let num = int.Parse(Parts[ 1 ])
group new { Name = item, Number = num } by item into Grp
select new {
Name = Grp.Key,
Value = Grp.Max(i => i.Number).ToString()
};
var highestGroups = q.Select(g => String.Format("{0} {1}", g.Name, g.Value));
MessageBox.Show(String.Join(Environment.NewLine, highestGroups));
The result:
Book 23
Notebook 23
Pen 23
sheet 3
You may want to add a custom comparer as a parameter, as you can see in the example on MSDN.
In this example I assumed Foo is a class with two members.
class Program
{
static void Main(string[] args)
{
var list = new List<Foo>()
{
new Foo("Book", 23),
new Foo("Book", 22),
new Foo("Book", 19)
};
foreach(var element in list.Distinct(new Comparer()))
{
Console.WriteLine(element.Type + " " + element.Value);
}
}
}
public class Foo
{
public Foo(string type, int value)
{
this.Type = type;
this.Value = value;
}
public string Type { get; private set; }
public int Value { get; private set; }
}
public class Comparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if(x == null || y == null)
return x == y;
else
return x.Type == y.Type;
}
public int GetHashCode(Foo obj)
{
return obj.Type.GetHashCode();
}
}
This works on an IList, assuming that we want the first item each, not the one with the highest number. Be careful with different collection types (like ICollection or IEnumerable), as they do not guarantee you any order. Therefore any of the Foos may remain after the Distinct.
You could also override both Equals and GetHashCode of Foo instead of using a custom IEqualityComparer. However, I would not actually recommend this for a local distinct. Consumers of your class may not recognize that two instances with same value for Type are always equal, regardless of their Value.
a bit old fashioned , but it should work ,
If I understand correctrly
Dictionary<string,int> dict=new Dictionary<string,int>();
//Split accepts 1 character ,assume each line containes key value pair seperated with spaces and not containing whitespaces
input=input.Replace("\r\n","\n");
string[] lines=input.Split('\n');
//break to categories and find largest number at each
foreach(line in lines)
{
string parts[]=line.Split(' ');
string key=parts[0].Trim();
int value=Convert.ToInt32(parts[1].Trim());
if (dict.ContainsKey(key))
{
dict.Add(key, value);
}
else
{
if (dict[key]<value)
{
dict[key]=value;
}
}
}
//do somethig with dict