Hello I have the following code which returns me List of type Listitem. I want to increase the value by one for every ListItem Selected.
public static List<System.Web.UI.WebControls.ListItem> GetMyCompassTUTListContent(List<int> ContentID, Int32 CountryID)
{
int Counter = 0;
List<System.Web.UI.WebControls.ListItem> litems = new List<System.Web.UI.WebControls.ListItem>();
using (DbDataContext objContext = new DbDataContext())
{
if (CountryID == (int)MyCompassBLL.Constants.Country.Australia)
{
litems = objContext.Contents.Where(x => ContentID.Contains(x.ID)).Select(x => new System.Web.UI.WebControls.ListItem { Text = x.Text, y = (y + 1) }).ToList();
}
else
{
litems = objContext.ContentCountries.Where(x => ContentID.Contains(x.ContentID) && x.CountryID == CountryID).Select(x => new System.Web.UI.WebControls.ListItem { Text = x.Text, Value = (Counter + 1).ToString() }).ToList();
}
}
return litems;
}
Please help me in this. I am not able to that since I am not able to find the way of how to reassign value to variable counter after increment.
Thanks in advance
There is an overload of Select that also provides the index of the current item. You can use that. However, most DB query providers don't support it, so you'll need to do your DB query, then ensure that the Enumerable overload of Select is called to add the index:
public static List<ListItem> GetMyCompassTUTListContent(
List<int> ContentID, Int32 CountryID)
{
//Note this is IEnumerable, not IQueryable, this is important.
IEnumerable<string> query;
using (DbDataContext objContext = new DbDataContext())
{
if (CountryID == (int)MyCompassBLL.Constants.Country.Australia)
{
query = objContext.Contents.Where(x => ContentID.Contains(x.ID))
.Select(x => x.Text);
}
else
{
query = objContext.ContentCountries
.Where(x => ContentID.Contains(x.ContentID)
&& x.CountryID == CountryID)
.Select(x => x.Text);
}
return query.Select((text, index) => new ListItem
{
Text = text,
Value = (index + 1).ToString(),
})
.ToList();
}
}
Use ++Counter instead of ( Counter + 1)
[UPDATE]
try to increment your Counter before put it into the Select():
else
{
Counter++;
litems = objContext.ContentCountries.Where(x => ContentID.Contains(x.ContentID) && x.CountryID == CountryID).Select(x => new System.Web.UI.WebControls.ListItem { Text = x.Text, Value = Counter.ToString() }).ToList();
}
Related
I have the below class:
public class FactoryOrder
{
public string Text { get; set; }
public int OrderNo { get; set; }
}
and collection holding the list of FactoryOrders
List<FactoryOrder>()
here is the sample data
FactoryOrder("Apple",20)
FactoryOrder("Orange",21)
FactoryOrder("WaterMelon",42)
FactoryOrder("JackFruit",51)
FactoryOrder("Grapes",71)
FactoryOrder("mango",72)
FactoryOrder("Cherry",73)
My requirement is to merge the Text of FactoryOrders where orderNo are in sequence and retain the lower orderNo for the merged FactoryOrder
- so the resulting output will be
FactoryOrder("Apple Orange",20) //Merged Apple and Orange and retained Lower OrderNo 20
FactoryOrder("WaterMelon",42)
FactoryOrder("JackFruit",51)
FactoryOrder("Grapes mango Cherry",71)//Merged Grapes,Mango,cherry and retained Lower OrderNo 71
I am new to Linq so not sure how to go about this. Any help or pointers would be appreciated
As commented, if your logic depends on consecutive items so heavily LINQ is not the easiest appoach. Use a simple loop.
You could order them first with LINQ: orders.OrderBy(x => x.OrderNo )
var consecutiveOrdernoGroups = new List<List<FactoryOrder>> { new List<FactoryOrder>() };
FactoryOrder lastOrder = null;
foreach (FactoryOrder order in orders.OrderBy(o => o.OrderNo))
{
if (lastOrder == null || lastOrder.OrderNo == order.OrderNo - 1)
consecutiveOrdernoGroups.Last().Add(order);
else
consecutiveOrdernoGroups.Add(new List<FactoryOrder> { order });
lastOrder = order;
}
Now you just need to build the list of FactoryOrder with the joined names for every group. This is where LINQ and String.Join can come in handy:
orders = consecutiveOrdernoGroups
.Select(list => new FactoryOrder
{
Text = String.Join(" ", list.Select(o => o.Text)),
OrderNo = list.First().OrderNo // is the minimum number
})
.ToList();
Result with your sample:
I'm not sure this can be done using a single comprehensible LINQ expression. What would work is a simple enumeration:
private static IEnumerable<FactoryOrder> Merge(IEnumerable<FactoryOrder> orders)
{
var enumerator = orders.OrderBy(x => x.OrderNo).GetEnumerator();
FactoryOrder previousOrder = null;
FactoryOrder mergedOrder = null;
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (mergedOrder == null)
{
mergedOrder = new FactoryOrder(current.Text, current.OrderNo);
}
else
{
if (current.OrderNo == previousOrder.OrderNo + 1)
{
mergedOrder.Text += current.Text;
}
else
{
yield return mergedOrder;
mergedOrder = new FactoryOrder(current.Text, current.OrderNo);
}
}
previousOrder = current;
}
if (mergedOrder != null)
yield return mergedOrder;
}
This assumes FactoryOrder has a constructor accepting Text and OrderNo.
Linq implementation using side effects:
var groupId = 0;
var previous = Int32.MinValue;
var grouped = GetItems()
.OrderBy(x => x.OrderNo)
.Select(x =>
{
var #group = x.OrderNo != previous + 1 ? (groupId = x.OrderNo) : groupId;
previous = x.OrderNo;
return new
{
GroupId = group,
Item = x
};
})
.GroupBy(x => x.GroupId)
.Select(x => new FactoryOrder(
String.Join(" ", x.Select(y => y.Item.Text).ToArray()),
x.Key))
.ToArray();
foreach (var item in grouped)
{
Console.WriteLine(item.Text + "\t" + item.OrderNo);
}
output:
Apple Orange 20
WaterMelon 42
JackFruit 51
Grapes mango Cherry 71
Or, eliminate the side effects by using a generator extension method
public static class IEnumerableExtensions
{
public static IEnumerable<IList<T>> MakeSets<T>(this IEnumerable<T> items, Func<T, T, bool> areInSameGroup)
{
var result = new List<T>();
foreach (var item in items)
{
if (!result.Any() || areInSameGroup(result[result.Count - 1], item))
{
result.Add(item);
continue;
}
yield return result;
result = new List<T> { item };
}
if (result.Any())
{
yield return result;
}
}
}
and your implementation becomes
var grouped = GetItems()
.OrderBy(x => x.OrderNo)
.MakeSets((prev, next) => next.OrderNo == prev.OrderNo + 1)
.Select(x => new FactoryOrder(
String.Join(" ", x.Select(y => y.Text).ToArray()),
x.First().OrderNo))
.ToList();
foreach (var item in grouped)
{
Console.WriteLine(item.Text + "\t" + item.OrderNo);
}
The output is the same but the code is easier to follow and maintain.
LINQ + sequential processing = Aggregate.
It's not said though that using Aggregate is always the best option. Sequential processing in a for(each) loop usually makes for better readable code (see Tim's answer). Anyway, here's a pure LINQ solution.
It loops through the orders and first collects them in a dictionary having the first Id of consecutive orders as Key, and a collection of orders as Value. Then it produces a result using string.Join:
Class:
class FactoryOrder
{
public FactoryOrder(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
}
The program:
IEnumerable<FactoryOrder> orders =
new[]
{
new FactoryOrder(20, "Apple"),
new FactoryOrder(21, "Orange"),
new FactoryOrder(22, "Pear"),
new FactoryOrder(42, "WaterMelon"),
new FactoryOrder(51, "JackFruit"),
new FactoryOrder(71, "Grapes"),
new FactoryOrder(72, "Mango"),
new FactoryOrder(73, "Cherry"),
};
var result = orders.OrderBy(t => t.Id).Aggregate(new Dictionary<int, List<FactoryOrder>>(),
(dir, curr) =>
{
var prevId = dir.SelectMany(d => d.Value.Select(v => v.Id))
.OrderBy(i => i).DefaultIfEmpty(-1)
.LastOrDefault();
var newKey = dir.Select(d => d.Key).OrderBy(i => i).LastOrDefault();
if (prevId == -1 || curr.Id - prevId > 1)
{
newKey = curr.Id;
}
if (!dir.ContainsKey(newKey))
{
dir[newKey] = new List<FactoryOrder>();
}
dir[newKey].Add(curr);
return dir;
}, c => c)
.Select(t => new
{
t.Key,
Items = string.Join(" ", t.Value.Select(v => v.Name))
}).ToList();
As you see, it's not really straightforward what happens here, and chances are that it performs badly when there are "many" items, because the growing dictionary is accessed over and over again.
Which is a long-winded way to say: don't use Aggregate.
Just coded a method, it's compact and quite good in terms of performance :
static List<FactoryOrder> MergeValues(List<FactoryOrder> dirtyList)
{
FactoryOrder[] temp1 = dirtyList.ToArray();
int index = -1;
for (int i = 1; i < temp1.Length; i++)
{
if (temp1[i].OrderNo - temp1[i - 1].OrderNo != 1) { index = -1; continue; }
if(index == -1 ) index = dirtyList.IndexOf(temp1[i - 1]);
dirtyList[index].Text += " " + temp1[i].Text;
dirtyList.Remove(temp1[i]);
}
return dirtyList;
}
I'm having this linq query which checks for all 6 dropdowns whether there is any repetition in selction.
To do that , currently I'm setting SelectedIndex to 0 for all dropdowns. Instead of that , i want those two dropdowns.SelectedIndex=0 which has same SelectedValue.
var allIndexes = new List<int>
{
drpdwnlst_Seq1.SelectedIndex,
drpdwnlst_Seq2.SelectedIndex,
drpdwnlst_Seq3.SelectedIndex,
drpdwnlst_Seq4.SelectedIndex,
drpdwnlst_Seq5.SelectedIndex,
drpdwnlst_Seq6.SelectedIndex
};
var noSelectedIndexIsTheSame = allIndexes.Where(x => x != 0)
.GroupBy(x => x)
.All(x => x.Count() == 1);
if (!noSelectedIndexIsTheSame)
{
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "AdminUserError", "alert('Selection Rrepeated.'); ", true);
drpdwnlst_Seq1.SelectedIndex = 0;
drpdwnlst_Seq2.SelectedIndex = 0;
drpdwnlst_Seq3.SelectedIndex = 0;
drpdwnlst_Seq4.SelectedIndex = 0;
drpdwnlst_Seq5.SelectedIndex = 0;
drpdwnlst_Seq6.SelectedIndex = 0;
}
You could change the List to the following definition, (so you can keep most of your logic)
List<KeyValuePair<DropDownList, int>> allIndexes = new List<KeyValuePair<DropDownList, int>>
{
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq1, drpdwnlst_Seq1.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq2, drpdwnlst_Seq2.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq3, drpdwnlst_Seq3.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq4, drpdwnlst_Seq4.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq5, drpdwnlst_Seq5.SelectedIndex),
new KeyValuePair<DropDownList, int>(drpdwnlst_Seq6, drpdwnlst_Seq6.SelectedIndex)
};
var groups = allIndexes.Where(x => x.Value != 0).GroupBy(x => x.Value);
boolean noSelectedIndexIsTheSame = groups.All(g => g.Count() == 1);
if (!noSelectedIndexIsTheSame)
{
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "AdminUserError", "alert('Selection Rrepeated.'); ", true);
foreach (var g in groups)
{
if (g.Count() == 1)
{
continue;
}
foreach (var kvp in g)
{
kvp.Key.SelectedIndex = 0;
}
}
}
this way, you have the reference of your combobox + the current selected index, you group by the KeyValuePair.Value, and you have a separate bool checking if no indexes are the same. If they are, it will loop the groups, and in case more than 1 was found per group, reset those
I am looking for simple LINQ to solve this:
string[] breakups = new[]
{
"YQ:50/BF:50/YR:50",
"YQ:50/SR:50",
"YQ:50/BF:50/YR:50",
"XX:00 .... and so on"
};
// LINQ expression
string expectedResult = "YQ:150/BF:100/YR:100/SR:50";
My alternate solution as follows
public static string CalcRTBreakup(string pstrBreakup)
{
string lstrBreakup = pstrBreakup;
try
{
string[] lstrArrRB = lstrBreakup.Split('#');
string[] lstrArrBreakupSplit = new string[lstrBreakup.Split('#')[0].Split('|').Length];
for (int count = 0; count < lstrArrRB.Length; count++)
{
string[] lstrArrtemp = lstrArrRB[count].Split('|');
for (int countinner = 0; countinner < lstrArrtemp.Length; countinner++)
{
if (string.IsNullOrEmpty(lstrArrBreakupSplit[countinner]))
{
if (string.IsNullOrEmpty(lstrArrtemp[countinner]))
continue;
lstrArrBreakupSplit[countinner] = lstrArrtemp[countinner];
}
else
{
if (string.IsNullOrEmpty(lstrArrtemp[countinner]))
continue;
lstrArrBreakupSplit[countinner] += "/" + lstrArrtemp[countinner];
}
}
}
for (int count = 0; count < lstrArrBreakupSplit.Length; count++)
{
if (string.IsNullOrEmpty(lstrArrBreakupSplit[count]))
continue;
lstrArrBreakupSplit[count] = CalcRTBreakupDict(lstrArrBreakupSplit[count].TrimEnd('/')).TrimEnd('/');
}
lstrBreakup = string.Empty;
foreach (string strtemp in lstrArrBreakupSplit)
{
lstrBreakup += strtemp + '|';
}
return lstrBreakup;
}
catch (Exception)
{
return "";
}
}
public static string CalcRTBreakupDict(string pstrBreakup)
{
string lstrBreakup = pstrBreakup;
Dictionary<string, double> ldictDreakup = new Dictionary<string, double>();
try
{
lstrBreakup = lstrBreakup.TrimEnd('/').Trim();
string[] lstrArrBreakup = lstrBreakup.Split('/');
foreach (string strBr in lstrArrBreakup)
{
string[] lstrBreakupCode = strBr.Split(':');
if (!ldictDreakup.Keys.Contains(lstrBreakupCode[0]))
{
double lintTemp = 0; double.TryParse(lstrBreakupCode[1], out lintTemp);
ldictDreakup.Add(lstrBreakupCode[0], lintTemp);
}
else
{
double lintTemp = 0; double.TryParse(lstrBreakupCode[1], out lintTemp);
lintTemp = lintTemp + ldictDreakup[lstrBreakupCode[0]];
ldictDreakup.Remove(lstrBreakupCode[0]);
ldictDreakup.Add(lstrBreakupCode[0], lintTemp);
}
}
lstrBreakup = string.Empty;
foreach (string dictKey in ldictDreakup.Keys)
{
lstrBreakup += dictKey + ":" + ldictDreakup[dictKey] + "/";
}
return lstrBreakup;
}
catch (Exception)
{
return pstrBreakup;
}
}
string[] breakups =
{
"YQ:50/BF:50/YR:50",
"YQ:50/SR:50",
"YQ:50/BF:50/YR:50",
"XX:00"
};
var groups = from line in breakups // these are our items in the array
from item in line.Split('/') // each one will be split up at '/'
let pair = item.Split(':') // each pair is split at ':'
let key = pair[0] // our key is the first item...
let value = int.Parse(pair[1]) // and the value is the second
group value by key // let's group by key
into singleGroup
let sum = singleGroup.Sum() // and build each group's sum
where sum > 0 // filter out everything <= 0
select singleGroup.Key + ":" + sum; // and build the string
var result = string.Join("/", groups);
If you don't need an ordering by value, you can simply do
var res = string.Join("/", breakups
.SelectMany(m => m.Split('/'))
.Select(x => x.Split(':'))
.GroupBy(m => m[0])
.Select(m => string.Format("{0}:{1}", m.Key, m.Sum(g => Int32.Parse(g[1])))));
if you need ordering
var res = string.Join("/", breakups
.SelectMany(m => m.Split('/'))
.Select(x => x.Split(':'))
.GroupBy(m => m[0])
.Select(m => new
{
key = m.Key,
val = m.Sum(g => Int32.Parse(g[1]))
})
.OrderByDescending(m => m.val)
.Select(m => string.Format("{0}:{1}", m.key, m.val)));
var sums = breakups.SelectMany(breakup => breakup.Split('/'))
.Select(s => new { Code = s.Substring(0, 2), Value = int.Parse(s.Substring(2)) })
.GroupBy(pair => pair.Code)
.Select(group => string.Format("{0}/{1}", group.Key, group.Sum(x => x.Value)));
string result = string.Join("/", sums);
The code may contain syntax errors, cause I've not tested it.
Looking at the result I am assuming you add the values after the semicolon where the label before the semicolon match.
It can be treated as a little fun quiz, maybe more suited to another stackexchange site, but anyway.
This can be achieved with a simple (but not very short) linq expression:
breakups
.Select(c => c.Split('/'))
.SelectMany(c => c)
.Select(c => new
{
Label = c.Split(':')[0],
Value = Convert.ToInt32(c.Split(':')[1])
})
.GroupBy(c => c.Label)
.Select(c => new
{
Label = c.Key,
Value = c.Sum(x => x.Value)
})
.OrderByDescending(c => c.Value)
.Select(c => c.Label + ":" + c.Value)
.Aggregate((s1,s2) => s1 + "/" + s2)
How can I perform a conditional select on a column value, where I have a preference over which value is returned. If I can't find the top choice, I settle on the next, if available, and then if not the next, etc. As it looks right now, it would take 3 total queries. Is there a way to simplify this further?
var myResult = string.Empty;
if (myTable.Where(x => x.ColumnValue == "Three").Any())
{
myResult = "Three"; // Can also be some list.First().Select(x => x.ColumnValue) if that makes it easier;
}
else if (myTable.Where(x => x.ColumnValue == "One").Any())
{
myResult = "One";
}
else if (myTable.Where(x => x.ColumnValue == "Two").Any())
{
myResult = "Two";
}
else
{
myResult = "Four";
}
You could use a string[] for your preferences:
string[] prefs = new[]{ "One", "Two", "Three" };
string myResult = prefs.FirstOrDefault(p => myTable.Any(x => x.ColumnValue == p));
if(myResult == null) myResult = "Four";
Edit Enumerable.Join is a very efficient hash table method, it also needs only one query:
string myResult = prefs.Select((pref, index) => new { pref, index })
.Join(myTable, xPref => xPref.pref, x => x.ColumnValue, (xPref, x) => new { xPref, x })
.OrderBy(x => x.xPref.index)
.Select(x => x.x.ColumnValue)
.DefaultIfEmpty("Four")
.First();
Demo
I wrote an extension method that effectively mirrors Tim Schmelter's answer (was testing this when he posted his update. :-()
public static T PreferredFirst<T>(this IEnumerable<T> data, IEnumerable<T> queryValues, T whenNone)
{
var matched = from d in data
join v in queryValues.Select((value,idx) => new {value, idx}) on d equals v.value
orderby v.idx
select new { d, v.idx };
var found = matched.FirstOrDefault();
return found != null ? found.d : whenNone;
}
// usage:
myResult = myTable.Select(x => x.ColumnValue)
.PreferredFirst(new [] {"Three", "One", "Two"}, "Four");
I've written one that will quit a little more early:
public static T PreferredFirst<T>(this IEnumerable<T> data, IList<T> orderBy, T whenNone)
{
// probably should consider a copy of orderBy if it can vary during runtime
var minIndex = int.MaxValue;
foreach(var d in data)
{
var idx = orderBy.IndexOf(d);
if (idx == 0) return d; // best case; quit now
if (idx > 0 && idx < minIndex) minIndex = idx;
}
// return the best found or "whenNone"
return minIndex == int.MaxValue ? whenNone : orderBy[minIndex];
}
I use a weighted approach in SQL where I assign a weight to each conditional value. The solution would then be found by finding the highest or lowest weight depending on your ordering scheme.
Below would be the equivalent LINQ query. Note that in this example I am assigning a lower weight a higher priority:
void Main()
{
// Assume below list is your dataset
var myList =new List<dynamic>(new []{
new {ColumnKey=1, ColumnValue ="Two"},
new {ColumnKey=2, ColumnValue ="Nine"},
new {ColumnKey=3, ColumnValue ="One"},
new {ColumnKey=4, ColumnValue ="Eight"}});
var result = myList.Select(p => new
{
ColVal = p.ColumnValue,
OrderKey = p.ColumnValue == "Three" ? 1 :
p.ColumnValue == "One" ? 2 :
p.ColumnValue == "Two" ? 3 : 4
}).Where(i=> i.OrderKey != 4)
.OrderBy(i=>i.OrderKey)
.Select(i=> i.ColVal)
.FirstOrDefault();
Console.WriteLine(result ?? "Four");
}
How about something like this:
var results = myTable.GroupBy(x => x.ColumnValue).ToList();
if (results.Contains("Three")) {
myResult = "Three";
} else if (results.Contains("One")) {
myResult = "One";
} else if (results.Contains("Two")) {
myResult = "Two";
} else {
myResult = "Four";
}
I have a program that generates 3 lists based on the contents of a text file. Now I want to look at a list and if there's an item in it more than once, I'd like to change the value to "number in list x item" and remove the duplicates from the list.
Here is the code I use to open and split up the file into the lists:
private void open_Click(object sender, EventArgs e)
{
if (inputFile.ShowDialog() == DialogResult.OK)
{
var reader = new StreamReader(File.OpenRead(inputFile.FileName));
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
if (string.IsNullOrEmpty(line)) continue;
if (line.StartsWith("#main"))
{
deck = "main";
}
if (deck == "main")
{
if (!line.StartsWith("#"))
{
int cardid = Convert.ToInt32(line.Substring(0));
MainDeck.Items.Add(Program.CardData[cardid].Name);
}
}
if (line.StartsWith("#extra"))
{
deck = "extra";
}
if (deck == "extra")
{
if (!line.StartsWith("#extra") && !line.StartsWith("!side"))
{
int cardid = Convert.ToInt32(line.Substring(0));
ExtraDeck.Items.Add(Program.CardData[cardid].Name);
}
}
if (line.StartsWith("!side"))
{
deck = "side";
}
if (deck == "side")
{
if (!line.StartsWith("!side"))
{
int cardid = Convert.ToInt32(line.Substring(0));
SideDeck.Items.Add(Program.CardData[cardid].Name);
}
}
}
reader.Close();
GenerateCode();
}
}
In other words say the item "hello" is in the list 3 times: I want to change it to be in the list only once and say "3x hello".
Use Enumerable.Distinct to remove the duplicates:
MainDeck = MainDeck.Distinct().ToList();
ExtraDeck = ExtraDeck.Distinct().ToList();
SideDeck = SideDeck.Distinct().ToList();
If you want to count the duplicates first:
int mainDeckDups = MainDeck.Count - MainDeck.Distinct().Count();
int extraDeckDups = ExtraDeck.Count - ExtraDeck.Distinct().Count();
int sideDeckDups = SideDeck.Count - SideDeck.Distinct().Count();
If you really want to show how many times an item was in the list you could use Enumerable.GroupBy, e.g.:
var mainDeckGroups = MainDeck.GroupBy(s => s)
.Select(g => new { Item = g.Key, Count = g.Count() })
.Where(x => x.Count > 1)
.OrderByDescending(x => x.Count);
foreach (var dup in mainDeckGroups)
Console.WriteLine("{0}x {1}", dup.Count, dup.Item);
// other lists ...
Something like:
var g = MainDeck.Items.GroupBy(i => i).Select(x => x.Count() +"x " + x.Key);
You can filter it out if you want to count only the word "main" there. Follow the same for other lists..
The query is not evaluated at that point. Do a .ToList() or .ToArray() to let that happen on g.
If you want to preserve order of items in the list (assuming list contains the possible duplicates)
var data = list.Select(r => result.Count(i => i == r) + "x " + r).ToList();
and just slightly modify it to have "Hello" instead of "1x Hello".
You can try it as well.
var list = new List<string> { "Hello", "World", "Hello", "Great", "World" };
var query = list.GroupBy(s => s)
.Select(g => new { Value = g.Key, Count = g.Count() });
Then,
var resultList = query.Select(result => string.Format("{0}x {1}", result.Count, result.Value)).ToList();
or above code can be replaced as below for more clarity
foreach (var result in query)
resultList.Add(string.Format("{0}x {1}", result.Count, result.Value));
Hope it helps.