I'm new with using Linq and was wondering how I could print out multiple values of my Mode value. At the minute I can only get 1 value from the Mode but I want it to show multiples ones.
string[] list = TextBox1.Text.Split(new string[] { "," },
StringSplitOptions.RemoveEmptyEntries);
int[] numbers = new int[list.Length];
for (int i = 0; i < numbers.Length; i++)
{
numbers[i] = Convert.ToInt32(list[i].Trim());
}
int mode = numbers.GroupBy(v => v)
.OrderByDescending(g => g.Count())
.First()
.Key;
You need to save off the collection before taking the item(s) you want.
string[] list = TextBox1.Text.Split(new string[] { "," },
StringSplitOptions.RemoveEmptyEntries);
IEnumerable<IGrouping<int, int>> modes = list.GroupBy(v => v);
IEnumerable<IGrouping<int, IGrouping<int, int>>> groupedModes = modes.GroupBy(v => v.Count());
var sortedGroupedModes = groupedModes.OrderByDescending(g => g.Key).ToList();
TextBox2.Text = string.Join(" ", sortedGroupedModes[0].Select(x => x.Key)));
You could get all of the groups and just extract those with the highest count (including ties):
var counts = numbers.GroupBy(v => v)
.Select(g => g.Key, Count = g.Count())
.OrderByDescending(g => g.Count);
var modes = numbers.Where(g => g.Count == counts.First().Count)
.Select(g => g.Key);
Related
I have a list of strings which contain X in them. I want to select list(s) with the minimum count of X in them. For example:
CountMin("AXBXX", "AAX") will return AAX.
How can I write this qith LINQ in a concise way ?
public static string CountMin(IList<string> inputList)
{
if (inputList == null || !inputList.Any()) return null;
var result = inputList.Select(s => new
{
Item = s,
Count => s.Count(ch => ch == 'X')
})
.OrderBy(item => item.Count).First().Item;
}
Snippet assumes that all elements on list are different to null. If you need it, it could be easily improved.
You can also omit temporary class:
inputList.OrderBy(s => s.Count(c => c == 'X')).First();
string[] list = {"AXBXX", "AAX", "AXX"};
string result = (from word in list
select new { word, wordLen = (word.Length - (word.Replace("X", "")).Length) })
.OrderBy(x => x.wordLen).First().word;
MessageBox.Show(result);
Here's an answer that will get you all of the minimum X strings from the list.
var listOfStrings = new List<string>()
{
"AXB",
"ABXXC",
"ABX",
};
var minimumXs =
listOfStrings
.GroupBy(x => x.Count(y => y == 'X'))
.OrderBy(x => x.Key)
.Take(1)
.SelectMany(x => x);
That gives me:
AXB
ABX
I have an list with x items. I wish to get an results that groups this list based of a number and not a property.
For example.
I have a list of 8 items. I want to group them by 3.
I want to get a List thats contains three lists, where the first two lists contains each three items and the last list the remaining two.
I want a more elegant solution than this:
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
var result = new List<List<TrimlinePage>>();
while (!(result.Count != 0 && result.Last().Count % 3 > 0))
{
int skip = result.Count*groupSize;
var group = pages.Skip(skip).Take(groupSize).ToList();
result.Add(group);
}
return result;
}
You can use the integer divison trick:
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index / groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Example:
int groupSize = 3;
var pages = new List<string> { "A", "B", "C", "D", "E", "F", "G" };
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index / groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Result:
foreach(var list in lists)
Console.WriteLine(string.Join(",", list));
Output:
A,B,C
D,E,F
G
So this approach will give you lists with the specified max-size, in this case 3. If you instead want to ensure that you always get three lists you need to use % instead of /:
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index % groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Try this:
var list = Enumerable.Range(1,100);
var query = list
.Select((x, i) => new {x, i})
.GroupBy(v => v.i / 3).Select(g => g.Select(v =>v.x.ToList()))
.ToList();
Here's a simple solution using side effects (which is generally discouraged):
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
var i = 0;
return pages.GroupBy(p => i++ / 3, (k, g) => g.ToList()).ToList();
}
Or if you want to avoid relying on side effects, you could use this:
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
return pages.Select(p => new { p, i })
.GroupBy(x => x.i / 3)
.Select(g => g.Select(x => x.p).ToList())
.ToList();
}
LINQ is not the best solution. Often good old indexing is much more readable and efficient.
private static List<List<T>> GroupBy(List<T> pages, int groupSize)
{
var result = new List<List<T>>();
List<T> l;
for (int i=0; i < pages.Count; i++)
{
if (i%groupSize == 0)
{
l = new List<T>();
result.Add(l);
}
l.Add(pages[i]);
}
return result;
}
You could also have a look at morelinq which contains the Partition method.
It's available via NuGet.
I have a string array[2] as follows:
1st Array 2nd Aray
"100101" "Testing123"
"100102" "Apple123"
"100101" "Dog123"
"100104" "Cat123"
"100101" "Animal123"
I would like to concatenate all elements of the 2nd array if the elements in the first array match.
For example elements of the first array that match are "100101", "100101" and "100101". So a string with the concatenated values of the respective 2nd array would be as follows:
"Testing123 Dog123 Animal123"
How could this be achieved elegantly?
I did it this way:
var results =
array1
.Zip(array2, (x1, x2) => new { x1, x2 })
.ToLookup(x => x.x1, x => x.x2)
.Select(x => new { x.Key, Value = String.Join(" ", x), });
I got this result:
If you needed to extract the results in a different way it wouldn't be too hard to fiddle with my method to get what you need.
You can use GroupBy:
var strings = array1.Select((s,index) => new{ s, index })
.GroupBy(x => x.s)
.Select(g =>
string.Join(" ", g.Select(x => array2.ElementAtOrDefault(x.index))));
foreach(string s in strings)
Console.WriteLine(s);
If you want to concatenate only strings which are duplicates in the first array, add this Where:
// ...
.GroupBy(x => x.s)
.Where(g => g.Count() > 1)
// ...
Here's a Demo
var indices = array1.Select((i, s) => new {Index = i, Str = s})
.Where(e => e.Str == "100101")
.Select(e => e.Index);
string result = string.Join(" ", array2.Select((i, s) => new {Index = i, Str = s})
.Where(e => indices.Contains(e.Index))
.Select(e => e.Str));
assuming both arrays are the same length, this should give you the output you need.
var array1 = new[] {"100101", "100102", "100101", "100104","100101" };
var array2 = new[] { "Testing123", "Apple123", "Dog123","Cat123", "Animal123" };
var result = new Dictionary<string, string>();
for (int i = 0; i < array1.Length; i++)
{
// if the value has been found before
if( result.ContainsKey( array1[i] ) ) {
result[array1[i]] += " " + array2[i]; // append to existing "matched" entry
}
else {
result.Add(array1[i], array2[i]); // add new unique value
}
}
You can zip these two arrays as they are of same size. Then group the elements by first array value.
Then join the elements.
I wrote a sample program using linq
string[] array1 = new string[]{"100101","100102","100101","100104","100101"};
string[] array2 = new string[] { "Testing123", "Apple123", "Dog123", "Cat123", "Animal123" };
var concatenatedString = array1.Zip(array2, (x, y) => new { First = x, Second = y }).GroupBy(t => t.First).Select(t=> string.Join(" ",t.Select(s=> s.Second))).ToList();
The result will contain a list of concatenated strings.
Hope it Helps
var arr1 = new [] { "100101", "100102", "100101", "100104", "100101" };
var arr2 = new [] { "Testing123", "Apple123", "Dog123", "Cat123", "Animal123" };
var result = string.Join(" ", arr2.Where((a, i) => i < arr1.Length && arr1[i] == "100101"));
I have an array of 2000 strings. The strings are: "art", "economy", "sport" and "politic". I want to group each 500 elements and get their counts
Could anyone help please?
Another solution:
var count = 0;
var dictionaries =
strings.GroupBy(s => count++ / 500)
.Select(g => g.Distinct().ToDictionary(k => k, k => g.Count(s => s == k)))
.ToList();
This will create a List<Dictionary<string, int>>. Each dictionary represents a tally of 500 elements (or possibly less for the last dictionary), where the keys are strings and the values are the number of occurrences of the string among the 500 elements the dictionary represents.
There is no requirement to hardcode all the possible values that may be encountered.
For the maximum possible performance you can also use this version:
var count = 0;
var dictionaries =
strings.GroupBy(s => count++ / 500)
.Select(g => g.Aggregate(
new Dictionary<string, int>(),
(d, w) => { d[w] = (d.ContainsKey(w) ? d[w] + 1 : 1); return d; })
)
.ToList();
This version iterates over each element in your source array exactly once. The output is in the same format as the first version.
var result = strings.Select((s, i) => new { s, i })
.GroupBy(x => x.i / 500)
.Select(x => x.GroupBy(y => y.s)
.Select(z => new {
Name=z.Key,
Count=z.Count()
}).ToList())
.ToList();
Try
var grouping = Enumerable.Range(0,2000)
.Select(i => i / 500)
.Zip(Strings, (i,s) => new { Group = i, Str = s})
.GroupBy(anon => anon.Group,
anon => anon.Str,
(key,g) => new
{
Key = key,
Art = g.Count(str => str == "art"),
Economy = g.Count(str => str == "economy"),
Politic = g.Count(str => str == "politic"),
Sport= g.Count(str => str == "sport")
});
foreach(anon in grouping)
{
//textbox logic OP will have to change to suit
TextBox1.WriteLine(String.Format("Group: {0}", anon.Key));
TextBox1.WriteLine(String.Format("Art: {0}",anon.Art));
TextBox1.WriteLine(String.Format("Economy: {0}",anon.Economy ));
TextBox1.WriteLine(String.Format("Politic: {0}",anon.Politic ));
TextBox1.WriteLine(String.Format("Sport: {0}",anon.Sport));
}
Alternatively (as per Snowbear)
var grouping = Strings.Select((s,i) => new { Group = i / 500, Str = s})
.GroupBy(anon => anon.Group,
anon => anon.Str,
(key,g) => new
{
Key = key,
Art = g.Count(str => str == "art"),
Economy = g.Count(str => str == "economy"),
Politic = g.Count(str => str == "politic"),
Sport= g.Count(str => str == "sport")
});
foreach(anon in grouping)
{
//textbox logic OP will have to change to suit
TextBox1.WriteLine(String.Format("Group: {0}",anon.Key + 1));
TextBox1.WriteLine(String.Format("Art: {0}",anon.Art));
TextBox1.WriteLine(String.Format("Economy: {0}",anon.Economy ));
TextBox1.WriteLine(String.Format("Politic: {0}",anon.Politic ));
TextBox1.WriteLine(String.Format("Sport: {0}",anon.Sport));
}
int CountElementsInGroup = 500;
//from 500 to 1000
int NumberGroup = 2;
string[] GroupTypes = new string[4] { "art", "economy", "sport", "politic" };
//Fill example array
string[] arr = new string[2000];
Random rand = new Random();
for (int i = 0; i < arr.Length;i++ )
arr[i] = GroupTypes[rand.Next(0, 3)];
var res = (from p in arr.Skip((NumberGroup - 1) * CountElementsInGroup).Take(CountElementsInGroup)
group p by p into g
select new GroupCountClass { GroupName = g.Key, GroupCount = g.Count() });
textBox1.Text = "";
foreach (GroupCountClass c in res)
{
textBox1.Text += String.Format("GroupName:{0} Count:{1};",c.GroupName,c.GroupCount);
}
List<string> str = new List<string>() {
"Alpha", "Beta", "Alpha", "Alpha", "Gamma", "Beta", "XYZ" };
Expected output:
String | Indexes
----------------------------
Alpha | 0, 2, 3
Beta | 1, 5
Gamma and XYZ are distinct so, they are ignored.
I've done this by comparing the strings manually. Would it be possible to do it using LINQ in more easier way?
foreach (var grp in
str.Select((s, i) => new { s, i })
.ToLookup(pair => pair.s, pair => pair.i)
.Where(pair => pair.Count() > 1))
{
Console.WriteLine("{0}: {1}", grp.Key, string.Join(", ", grp));
}
Something like this should work:
var elements = str
.Select((Elem, Idx) => new {Elem, Idx})
.GroupBy(x => x.Elem)
.Where(x => x.Count() > 1);
If you want to get a Dictionary<string,List<int>> having the duplicated string as key and the indexes as value, just add
.ToDictionary(x => x.Key, x => x.Select(e => e.Idx).ToList() );
after Where()
You can get the non-distinct strings by grouping, then you can get the index for each non-distinct string and group them to create an array for each string:
var distinct = new HashSet<string>(
str.GroupBy(s => s)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
);
var index =
str.Select((s, i) => new {
Str = s,
Index = i
})
.Where(s => distinct.Contains(s.Str))
.GroupBy(i => i.Str).Select(g => new {
Str = g.Key,
Index = g.Select(s => s.Index).ToArray()
});
foreach (var i in index) {
Console.WriteLine("{0} : {1}", i.Str, String.Join(", ", i.Index.Select(n => n.ToString())));
}
Output:
Alpha : 0, 2, 3
Beta : 1, 5