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
Related
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);
}
}
}
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 have two files that are being read into separate arrays such as:
String[] leaseName2 = new String[1000];
String[] fieldName2 = new String[1000];
String[] reservoir2 = new String[1000];
String[] operator2 = new String[1000];
String[] county2 = new String[1000];
String[] state2 = new String[1000];
String[] majo2 = new String[1000];
String[] resvCatgory2 = new String[1000];
String[] netOil2 = new String[1000];
String[] netGas2 = new String[1000];
String[] netNGL2 = new String[1000];
String[] leaseName = new String[1000];
String[] fieldName = new String[1000];
String[] reservoir = new String[1000];
String[] operator1 = new String[1000];
String[] county = new String[1000];
String[] state = new String[1000];
String[] majo = new String[1000];
String[] resvCatgory = new String[1000];
String[] netOil = new String[1000];
String[] netGas = new String[1000];
String[] netNGL = new String[1000];
I then merge the two files using Linq, to merge two of the matching arrays such as netOil and netOil2 that give me a double answer. There are a bunch of different rows of data that are matched by the same seqNum arrays.
String[] seqNum2 = new String[1000]; //This will be the identifier
String[] seqNum = new String[1000]; //This will be the identifier
The problem I am having is outputting all of the corresponding data such as leaseName[] and fieldname[] and reservoir[] with the identifying seqNum array. Here is my Linq code:
private void executeBtn_Click(object sender, EventArgs e)
{
//NET OIL VARIANCE MATHEMATICS
if (netOilRadBtn.Checked)
{
using (var sw = new StreamWriter("testNetOil.csv"))
{
var items = netOil.Zip(seqNum, (oil, seq) => new {Oil = oil, Seq = seq });
var items2 = netOil2.Zip(seqNum2, (oil, seq) => new { Oil = oil, Seq = seq });
sw.WriteLine("Lease Name, Field Name, Reservoir, Operator, County, ST, Majo, Resv Cat, Discount Rate, Net Oil Interest, Net Gas Interest, Working Interest, Gross Wells, Ultimate Oil, Ultimate Gas, Gross Oil, Gross NGL, Gross Gas, Net Oil, Net Gas, Net NGL, Revenue To Int., Oper. Expense, Total Invest., Revenue Oil, Revenue Gas, Operating Profit, Revenue NGL, Disc Net Income, SEQ, Well ID, INC ASN, Life Years, Own Qual, Production Tax, NET OIL VARIANCE");
foreach (var item in items2.Join(items, i => i.Seq, i => i.Seq, (a, b) => new
{
SeqID = a.Seq,
Answer = this.GetTheAnswer(Convert.ToDouble(a.Oil), Convert.ToDouble(b.Oil)),
//OilNum1 = a.Oil, GIVES FIRST OIL FROM NET OIL ARRAY 1, item.oilnum1 will print out first oil #
//OilNum2 = b.Oil, GIVES SECOND OIL FROM NET OIL ARRAY 2, item.OilNum2 ********
}))
{
sw.WriteLine(item.SeqID + "," + item.Answer); //this prints out the seqNum and Answer that I want to match with all of the other data in the arrays
/* commented out to see what I've tried
int x = listHead;
x.Equals(item.SeqID);
while (x != -1)
{
sw.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}",
QuoteString(leaseName[x]), fieldName[x], QuoteString2(reservoir[x]), operator1[x], county[x], state[x], majo[x], resvCatgory[x], disRate[x], netOil2Int[x], netGas2Int[x], workingInt[x], grossWells[x]
, ultOil[x], ultGas[x], grossOil[x], grossNGL[x], grossGas[x], netOil[x], netGas[x], netNGL[x], revToInt[x], operExpense[x], totInvest[x], revOil[x], revGas[x], operatingProfit[x],
revNGL[x], discNetIncome[x], seqNum[x], wellID[x], incASN[x], lifeYears[x], ownQual[x], prodTax[x], item.SeqID, item.Answer);
x = pointers[x];
//sw.WriteLine(item);
}*/
}
sw.Close();
}
}
I am not able to print out all of the data with the matching seqNum in the foreach loop, so I tried to create another loop but that was printing out a lot of extra data that was not useful. If anyone has any idea on how to print out all of the data with the seqNum after I get the Answer with the Linq code, I would appreciate it if you could let me know. Any help would be greatly appreciated.
I would suggest that you learn and practice some basic intro programming and OOP before attempting to use C# and LinQ.
First of all, the sole idea of having 11 arrays of strings is disgusting. You should learn how to create a proper data model and do so.
public class MyRecord
{
public int SeqNum {get;set;} // Notice the proper case in property names
public string LeaseName {get;set;}
public string FieldName {get;set;}
public string Reservoir {get;set;}
//... Etc.
}
Then, take a second to analyze whether it is a good idea that ALL your properties be string. For example, if a property can only have numeric values, it would be better that you strongly type that and use int or double. If a property can only have "true" or "false" values, that would be a bool, and so on.
The problem I am having is outputting all of the corresponding data
such as leaseName[] and fieldname[] and reservoir[] with the
identifying seqNum array.
That's because your data is modeled in the worst possible way. If you create a class like above, you will not have that problem, because all data pertaining to the same record will be in the same instance of this class. Then:
List<MyRecord> File1 {get;set;}
List<MyRecord> File2 {get;set;}
var myrecord = File1.FirstOrDefault(x => x.SeqNum == someSeqNum);
var leasename = myrecord.LeaseName;
var reservoir = myrecords.Reservoir;
//... etc
See how easy life actually is?
To top it all, PLEASE, for the sake of mankind, REMOVE your business logic from the code behind the UI. Create proper SERVICES to do that, with relevant methods with relevant parameters, in order to isolate this functionality and be able to REUSE it.
private void executeBtn_Click(object sender, EventArgs e)
{
MyService.ProcessRecords(relevant,parameters,from,the,UI);
}
How to add a comma separated string to an ArrayList? My string could hold 1 or many items which I'd like to add to ArrayList, each item combine with it's own id value separated by underscore (_) so it must be separated arraylist items..
e.g :
string supplierIdWithProducts = "1_1001,1_1002,20_1003,100_1005,100_1006";
ArrayList myArrayList= new ArrayList();
myArrayList.Add("1001,1002"); // 1
myArrayList.Add("1003"); // 20
myArrayList.Add("1005,1006"); // 100
After the ArrayList has been populated, I'd like pass it to a web service
that part is ok for me
foreach (string item in myArrayList){}
How could i do this...
Thanks..
string supplierIdWithProducts = "1_1001,1_1002,20_1003,100_1005,100_1006";
var lookup =
supplierIdWithProducts.Split(',')
.ToLookup(id => id.Split('_')[0],
id => id.Split('_')[1]);
foreach (var grp in lookup)
{
Console.WriteLine("{0} - {1}", grp.Key, string.Join(", ", grp));
}
will print:
1 - 1001, 1002
20 - 1003
100 - 1005, 1006
Firstly, I suggest you try to use a Dictionary or any other generic collection instead of an ArrayList to make it type-safe. Then use a string.Split(char c) and start the processing from there.
Here's an idea on how you can do it. It might get shorter with Extension methods of course. But here's just a thought-process on how you can do it.
static void ParseSupplierIdWithProducts()
{
string supplierIdWithProducts = "1_1001,1_1002,20_1003,100_1005,100_1006";
//eg. [0] = "1_1001", [1] = "1_1002", etc
List<string> supplierIdAndProductsListSeparatedByUnderscore = supplierIdWithProducts.Split(',').ToList();
//this will be the placeholder for each product ID with multiple products in them
//eg. [0] = key:"1", value(s):["1001", "1002"]
// [1] = key:"20", value(s):["1003"]
Dictionary<string, List<string>> supplierIdWithProductsDict = new Dictionary<string, List<string>>();
foreach (string s in supplierIdAndProductsListSeparatedByUnderscore)
{
string key = s.Split('_')[0];
string value = s.Split('_')[1];
List<string> val = null;
//look if the supplier ID is present
if (supplierIdWithProductsDict.TryGetValue(key, out val))
{
if (val == null)
{
//the supplier ID is present but the values are null
supplierIdWithProductsDict[key] = new List<string> { value };
}
else
{
supplierIdWithProductsDict[key].Add(value);
}
}
else
{
//that supplier ID is not present, add it and the value/product
supplierIdWithProductsDict.Add(key, new List<string> { value });
}
}
}
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