How to select non-distinct elements along with their indexes - c#

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

Related

Group list of strings by most common case variant

I have a function that is given a list of strings from a database to display as options in a select filter.
It handles case variants with StringComparer.InvariantCultureIgnoreCase.
public static List<string> GetMostCommonItems(List<string> values)
{
var filterData = values.Where(rawValue => !string.IsNullOrEmpty(rawValue))
.GroupBy(item => item.ToLower())
.ToDictionary(g => g.Key, g => g.GroupBy(gx => gx, StringComparer.InvariantCultureIgnoreCase).ToDictionary(gy => gy.Key, gy => gy.Count()));
var data = filterData.Select(element => element.Value.OrderByDescending(a => a.Value).FirstOrDefault().Key).OrderBy(c => c).ToList();
if (values.FirstOrDefault(x => string.IsNullOrEmpty(x)) != null)
{
data.Add(null);
}
return data;
}
Now when I load the list of options it will display only lower case if there is any,
if not Capital case if there is any, if not UPPER CASE.
I would like to count all lower, capital and upper case variants and only add the highest occurrence.
Here's my solution:
List<string> values = new List<string>{
"aaa", "aaa", "Aaa", "BBB", "BBB", "bbb"
};
var filterData = values
.Where(rawValue => !string.IsNullOrEmpty(rawValue))
.GroupBy(gx => gx, StringComparer.InvariantCultureIgnoreCase)
.Select(g => new { g.Key, Best = g.GroupBy(x => x).Select( g2 => new { g2.Key, Count = g2.Count() }).OrderByDescending( x => x.Count).FirstOrDefault() });
foreach(var d in filterData)
{
Console.WriteLine($"{d.Best.Key} # {d.Best.Count}");
}
This prints:
aaa # 2
BBB # 2

Calculate Mode Using LINQ C#

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);

Concatinating values of a matching string in an arrray

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"));

How to build a histogram for a list of int in C# [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Find the most frequent numbers in an array using LINQ
I have a list of int, List<int> demoList, which is something like {1, 2, 1, 1, 1, 3, 2, 1} and I want to write a LINQ statement for obtaining the number with the highest number of appearences from that list, which in my case is 1.
int highestAppearanceNum = demoList.GroupBy(i => i)
.OrderByDescending(grp => grp.Count())
.Select(grp => grp.First())
.First();
Edit: If you also want to know which number appears how often:
var appearances = demoList.GroupBy(i => i)
.OrderByDescending(grp => grp.Count())
.Select(grp => new { Num = grp.Key, Count = grp.Count() });
if (appearances.Any())
{
int highestAppearanceNum = appearances.First().Num; // 1
int highestAppearanceCount = appearances.First().Count; // 5
}
var list = new[] { 1, 2, 1, 1, 1, 3, 2, 1 };
var result = list
.GroupBy(x => x)
.Select(x => new { Number = x.Key, Count = x.Count() })
.OrderByDescending(x => x.Count)
.FirstOrDefault();
Console.WriteLine("highest number = {0}, count = {1}", result.Number, result.Count);
Use group by clause.
var groups =
from i in demoList
group i by i into g
select new { Value = g.Key, Count = g.Count() }
From here you can say
var max = groups.Max(g => g.Count);
groups.Where(g => g.Count == max).Select (g => g.Value); // { 1 }
var query =
from i in demoList
group i by i into g
orderby g.Count() descending
select new { Value = g.Key, Count = g.Count() };
var result = query.First();
Console.WriteLine(
"The number with the most occurrences is {0}, which appears {1} times",
result.Value,
result.Count);
I appologise in advance:
List<int> demoList = new List<int>() { 1, 2, 1, 1, 1, 3, 2, 1 };
Dictionary<int,int> keyOrdered = demoList.GroupBy(i => i)
.Select(i => new { i.Key, Count = i.Count() })
.OrderBy(i=>i.Key)
.ToDictionary(i=>i.Key, i=>i.Count);
var max = keyOrdered.OrderByDescending(i=>i.Value).FirstOrDefault();
List<string> histogram = new List<string>();
for (int i = max.Value; i >-1 ; i--)
{
histogram.Add(string.Concat(keyOrdered.Select(t => t.Value>i?"| ":" ")));
}
histogram.Add(string.Concat(keyOrdered.Keys.OrderBy(i => i).Select(i => i.ToString() + " ")));
histogram.ForEach(i => Console.WriteLine(i));
Console.WriteLine(Environment.NewLine);
Console.WriteLine("Max: {0}, Count:{1}", max.Key, max.Value);
when i read the title i thought of this and it made me smile.. (prolly full of bugs too!)

How to get distinct with highest value using Linq

Let's say I have following data:
Name Priority
A 3
A 5
B 1
C 1
C 3
C 2
I want to get list of distinct names with highest priority, so the result would look like:
Name Priority
A 5
B 1
C 3
How can I use Linq to do that?
var query = yourData
.GroupBy(x => x.Name,
(k, g) => g.Aggregate((a, x) => (x.Priority > a.Priority) ? x : a));
// and a quick test...
foreach (var result in query)
{
Console.WriteLine(result.Name + " " + result.Priority);
}
Here's an alternative approach:
var items = new List<Tuple<string, int>>()
{
Tuple.Create("A", 3),
Tuple.Create("A", 5),
Tuple.Create("B", 1),
Tuple.Create("C", 1),
Tuple.Create("C", 3),
Tuple.Create("C", 2)
};
var results = items.GroupBy(i => i.Item1)
.SelectMany(g => g
.Where(i => i.Item2 == g.Max(m => m.Item2)))
.Distinct();
Or if you prefer using the C# LINQ syntax:
results = (from item in items
group item by item.Item1 into groupedItems
let maxPriority = groupedItems.Max(item => item.Item2)
from element in groupedItems
where element.Item2 == maxPriority
select element).Distinct();
Another simple approach without aggregating
var listNP = new List<NP>()
{
new NP() {name="A",priority=3},
new NP() {name="A",priority=5},
new NP() {name="b",priority=1},
new NP() {name="b",priority=1},
new NP() {name="c",priority=3},
new NP() {name="c",priority=2},
};
var np = listNP.GroupBy(x => x.name).Select(y => new
{
name = y.Key,
max = y.Max(x=>x.priority)
}).ToList();
Update:
var np = listNP.GroupBy(x => x.name)
.Select(y => y.OrderByDescending(z => z.priority).First()).ToList();

Categories

Resources