I would like to create a
Dictionary<string, int[]> dict
out of two arrays:
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
the result:
["A"] = {1,5}
["B"] = {2}
["D"] = {2}
Is there a way i can do this with LINQ?
I have read about Zip but I don't think I can use since I need to add values to an existing key.value array.
Use .Zip to bind the two collections together and then GroupBy to group the keys.
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
var result = keys.Zip(values, (k, v) => new { k, v })
.GroupBy(item => item.k, selection => selection.v)
.ToDictionary(key => key.Key, value => value.ToArray());
Then to add these items into the dictionary that you already have:
I changed the int[] to List<int> so it is easier to handle Add/AddRange
Dictionary<string, List<int>> existingDictionary = new Dictionary<string, List<int>>();
foreach (var item in result)
{
if (existingDictionary.ContainsKey(item.Key))
existingDictionary[item.Key].AddRange(item.Value);
else
existingDictionary.Add(item.Key, item.Value.ToList());
}
Linq solution:
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
Dictionary<string, int[]> dict = keys
.Zip(values, (k, v) => new {
key = k,
value = v })
.GroupBy(pair => pair.key, pair => pair.value)
.ToDictionary(chunk => chunk.Key,
chunk => chunk.ToArray());
Test:
string report = String.Join(Environment.NewLine, dict
.Select(pair => $"{pair.Key} [{string.Join(", ", pair.Value)}]"));
Console.Write(report);
Outcome:
A [1, 5]
B [2]
D [2]
Try this :
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
Dictionary<string, int[]> dict = keys.Select((x, i) => new { key = x, value = values[i] }).GroupBy(x => x.key, y => y.value).ToDictionary(x => x.Key, y => y.ToArray());
Related
Hi I am trying to write a C# (Visual Studio) program for a function that takes integers in an array , and returns an array of integers that contains those integers which are most common in the input array.
sample in out -
[1,2,3,4,3,3,2,2,4] result = [2,3]
[1, 2, 3, 4, 5, 1, 6, 7, 1, 1] result = [1]
[1, 2, 3, 4, 5, 6, 7] result = [1, 2, 3, 4, 5, 6, 7]
I am almost there, but not getting expected results. Below is the code I wrote and I am a beginner.
namespace StringCommonElements
{
class Program
{
static void Main(string[] args)
{
// Compute frequencies for this data.
string[] values = { "bird", "cat", "bird", "dog", "bird", "man", "frog", "cat" };
// Get a list.
List<string> valuesList = new List<string>(values);
// Call our methods.
var freqs = GetFrequencies(valuesList);
DisplaySortedFrequencies(freqs);
}
static Dictionary<string, int> GetFrequencies(List<string> values)
{
var result = new Dictionary<string, int>();
foreach (string value in values)
{
if (result.TryGetValue(value, out int count))
{
// Increase existing value.
result[value] = count + 1;
}
else
{
// New value, set to 1.
result.Add(value, 1);
}
}
// Return the dictionary.
return result;
}
static void DisplaySortedFrequencies(Dictionary<string, int> frequencies)
{
// Order pairs in dictionary from high to low frequency.
var sorted = from pair in frequencies
orderby pair.Value descending
select pair;
// Display all results in order.
foreach (var pair in sorted)
{
Console.WriteLine($"{pair.Key} = {pair.Value}");
}
}
}
}
Here's how you can do that with Linq. That will group the numbers and find the count of each, then take all the ones that show up more than once. Then if the result is empty just return the original array because all the numbers are unique.
public int[] MostCommon(int[] numbers)
{
var ans = numbers
.GroupBy(x => x)
.Select(x => new {x.Key, x.Count}))
.Where(x => x.Count > 1)
.Select(x => x.Key)
.ToArray();
return ans.Length > 0 ? ans : numbers;
}
In case you were just wondering how to make your existing code work, all you have to do is return (or output) the items that have the same frequency as the one with the maximum frequency.
For example:
var maxFrequency = sorted.First().Value;
Console.WriteLine("These items all occur the most:");
foreach (var pair in sorted)
{
if (pair.Value < maxFrequency) break;
Console.WriteLine($" - {pair.Key} = {pair.Value}");
}
To get the single most occurring number, you can use this LINQ expression (this will return 3, even though 2 appears just as much):
int appearsMost = new int[] { 1, 2, 3, 4, 3, 3, 2, 2, 4 }
.GroupBy(x => x)
.Select(x => (Key: x.Key, Items: x.ToList()))
.OrderByDescending(x => x.Items.Count)
.First().Key;
The select clause with the ToList() in between is to prevent having to count() the grouped items multiple times.
The following solution should give you the numbers that appear most. (this will return 2 and 3)
int[] appearMost = new int[] { 1, 2, 3, 4, 3, 3, 2, 2, 4 }
.GroupBy(x => x)
.Select(x => (Key: x.Key, Items: x.ToList()))
.GroupBy(x => x.Items.Count)
.OrderByDescending(x => x.Key)
.First()
.Select(x => x.Key)
.ToArray();
If you want all numbers that appear more than once: (this will return 2,3,4,1)
int[] appearMoreThanOnce = new int[] { 1, 2, 3, 4, 3, 3, 2, 2, 4 }
.GroupBy(x => x)
.Select(x => (Key: x.Key, Items: x.ToList()))
.OrderByDescending(x => x.Items.Count)
.Where(x => x.Items.Count >= 1).Select(x => x.Key).ToArray();
In all cases, you can do the same by animals directly (only with small adjustments):
string[] animalsThatAppearMoreThanOnce = new string[] { "bird", "cat", "bird", "dog", "bird", "man", "frog", "cat" }
.GroupBy(x => x)
.OrderByDescending(x => x.Count())
.Where(x => x.Count() >= 1).Select(x => x.Key).ToArray();
// I added another cat, so this will return 'bird' and 'cat'.
string[] animalsThatAppearMost = new string[] { "bird", "cat", "bird", "dog", "bird", "man", "frog", "cat", "cat" }
.GroupBy(x => x)
.Select(x => (Key: x.Key, Items: x.ToList()))
.GroupBy(x => x.Items.Count)
.First()
.Select(x => x.Key)
.ToArray();
Actually, your methods work. I think it is just a matter of display. In order to emulate the examples that you posted, I would just write the DisplaySortedFrequencies this way:
static void DisplaySortedFrequencies(Dictionary<string, int> frequencies)
{
// Order pairs in dictionary from high to low frequency.
var sorted = from pair in frequencies
orderby pair.Value descending
select pair;
// Display all results in order.
int MaxNumValues = sorted.First().Value;
foreach (var pair in sorted)
{
int numValues = pair.Value;
if (numValues < MaxNumValues) //Show only the most predominant groups
break;
Console.WriteLine($"{pair.Key} = {numValues}");
}
}
In any case, if it is code that has to be performant, I would go for the solutions already posted that use Linq. I you don't want to use Linq for some reason, I would suggest to sort the items first and then count equal elements in a row instead of using a dictionary search for every element.
I have made a few changes to your code.
static void Main(string[] args)
{
// Compute frequencies for this data.
string[] values = { "bird", "cat", "bird", "dog", "bird", "man", "frog", "cat" };
var freqs = GetFrequencies(values);
DisplaySortedFrequencies(freqs);
}
static Dictionary<string, int> GetFrequencies(IEnumerable<string> values)
{
if (values == null) return new Dictionary<string, int>();
var maxCount = 1;
var result = new Dictionary<string, int>();
foreach (string value in values)
{
if (result.TryGetValue(value, out int count))
{
result[value] = count + 1;
if (maxCount < result[value])
{
maxCount = result[value];
}
}
else
{
result.Add(value, 1);
}
}
return result
.Where(item => item.Value == maxCount)
.OrderBy(item => item.Key)
.ToDictionary(item => item.Key, item => item.Value);
}
static void DisplaySortedFrequencies(Dictionary<string, int> frequencies)
{
foreach (var pair in frequencies)
{
Console.WriteLine($"{pair.Key} = {pair.Value}");
}
}
This i probably a simple question, but I'm still new to C# and LINQ (which I assume is useful in this case).
I have a List with different groups:
e.g. List<string>() { a, a, b, c, a, b, b };
I would like to make a corresponding List (sort of GroupID), holding:
List<int>() { 1, 1, 2, 3, 1, 2, 2}
The amount of different groups could be anything from 1-x, so a dynamic generation of the List is needed. Duplicate groups should get same numbers.
All this should end up in a LINQ zip() of the two into 'CombinedList', and a SqlBulkCopy with other data to a database with a foreach.
table.Rows.Add(data1, data2, data3, CombinedList.GroupID.toString() ,CombinedList.Group.ToString());
Hope it makes sense.
Example:
List<string>() { a, a, b, c, a, b, b };
This list holds 3 unique groups: a, b and c.
assign an incrementing number to the groups, starting from 1:
a = 1, b = 2, c = 3.
The generated result list should then hold
List<string>() { 1, 1, 2, 3, 1, 2, 2 };
This works for me:
var source = new List<string>() { "a", "a", "b", "c", "a", "b", "b" };
var map =
source
.Distinct()
.Select((x, n) => new { x, n })
.ToDictionary(xn => xn.x, xn => xn.n + 1);
var result =
source
.Select(x => new { GroupIP = map[x], Value = x });
I get this result:
Generate a List:
var strings = new List<string>() { "a", "a", "b", "c", "a", "b", "b" };
var uniqueStrings = strings.Distinct().ToList();
var numbers = strings.Select(s => uniqueStrings.IndexOf(s)).ToList();
This produces:
List<int> { 0, 0, 1, 2, 0, 1, 1 }
If you want to have your values starting at 1 instead of 0, then modify the last line to include +1 as per below:
var numbers = strings.Select(s => uniqueStrings.IndexOf(s) + 1).ToList();
Not sure but I think this can help you.
var keyValue = new List<KeyValuePair<int, string>>();
var listString = new List<string>() { "a", "a", "b", "c", "a", "b", "b"};
var listInt = new List<int>();
int count = 1;
foreach (var item in listString)
{
if(keyValue.Count(c=>c.Value == item) == 0)
{
keyValue.Add(new KeyValuePair<int, string>(count, item));
count++;
}
}
foreach (var item in listString)
{
listInt.Add(keyValue.Single(s=>s.Value == item).Key);
}
How about this:
int groupId = 1;
var list = new List<string>() { "a", "a", "b", "c", "a", "b", "b" };
var dict = list
.GroupBy(x => x)
.Select(x => new {Id = groupId++, Val = x})
.ToDictionary(x => x.Val.First(), x => x.Id);
var result = list.Select(x => dict[x]).ToList();
I have several arrays, like:
var arr1 = new[] { "A", "B", "C", "D" };
var arr2 = new[] { "A", "D" };
var arr3 = new[] { "A", "B", };
var arr4 = new[] { "C", "D" };
var arr5 = new[] { "B", "C", "D" };
var arr6 = new[] { "B", "A", };
... etc.
How can I get most common combination of elements in all of those arrays?
In this case it is A and B, because they occur in arr1, arr3 and arr6, and C and D, because they occur in arrays arr1, arr4 and arr5.
Just to mention that elements can be in any kind of collection, ie. in ArrayLists also.
UPDATE uuhhh, I was not clear enough...... Most common combinations of two elements in an array. That's what I tried to show in example, but did not mention in my question.
Sorry
:-((
If you are sure that each item appears only once in each array, you could just concatenate them together and get the counts, for example:
var arrs = new[] { arr1, arr2, arr3, arr4, arr5, arr6 };
var intermediate = arrs.SelectMany(a => a)
.GroupBy(x => x)
.Select(g => new { g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count);
var maxCount = intermediate.First().Count;
var results = intermediate.TakeWhile(x => x.Count == maxCount);
Or if you prefer query syntax, that would be:
var arrs = new[] { arr1, arr2, arr3, arr4, arr5, arr6 };
var intermediate =
from a in arrs.SelectMany(a => a)
group a by a into g
orderby g.Count() descending
select new { g.Key, Count = g.Count() };
var maxCount = intermediate.First().Count;
var results = intermediate.TakeWhile(x => x.Count == maxCount);
The result set will contain 3 items:
Key, Count
"A", 4
"B", 4
"D", 4
Update
Given your updated question, something like this should work:
var items = arrs.SelectMany(a => a).Distinct();
var pairs =
from a in items
from b in items
where a.CompareTo(b) < 0
select new { a, b };
var results =
(from arr in arrs
from p in pairs
where arr.Contains(p.a) && arr.Contains(p.b)
group arr by p into g
orderby g.Count() descending
select g.Key)
.First();
The logic here is:
First find all distinct items in any array
Then find every pair of items to search for
Get of every pair, grouped by a list of what arrays contain that pair
Order by the groups by the number of arrays that contain each pair, descending
Return the first pair
use a Dictionary which will store an element as an index, and the occurrence count as a value. Iterate each list and count the occurrences.
var arr1 = new[] { "A", "B", "C", "D" };
var arr2 = new[] { "A", "D" };
var arr3 = new[] { "A", "B", };
var arr4 = new[] { "C", "D" };
var arr5 = new[] { "B", "C", "D" };
var arr6 = new[] { "B", "A", };
var results = new List<IEnumerable<string>>() { arr1, arr2, arr3, arr4, arr5, arr6 }
.Select(arr => arr.Distinct())
.SelectMany(s => s)
.GroupBy(s => s)
.Select(grp => new { Text = grp.Key, Count = grp.Count() })
.OrderByDescending(t => t.Count)
.ToList();
Gives you {A, 4}, {B, 4}, {D, 4}, {C, 3}
var result = new IEnumerable<String>[] {arr1, arr2, arr3, arr4, arr5, arr6}
.SelectMany(a => a)
.GroupBy(s => s)
.GroupBy(g => g.Count())
.OrderByDescending(g => g.Key)
.FirstOrDefault()
.SelectMany(g => g.Key);
Your question is unclear as you have not clearly defined what you are looking for. In general, you could combine all the arrays into one large array and count the distinct elements. By then ordering the elements you can do whatever you intend to do with the "most common".
static void Main()
{
var arr1 = new[] { "A", "B", "C", "D" };
var arr2 = new[] { "A", "D" };
var arr3 = new[] { "A", "B", };
var arr4 = new[] { "C", "D" };
var arr5 = new[] { "B", "C", "D" };
var arr6 = new[] { "B", "A", };
List<string> combined = Combine(arr1, arr2, arr3, arr4, arr5, arr6);
var ordered = combined.OrderBy(i => i);//sorted list will probably help other functions work more quickly such as distinct
var distinct = ordered.Distinct();
var counts = new Dictionary<string, int>();
foreach (var element in distinct)
{
var count = ordered.Count(i => i == element);
counts.Add(element, count);
}
var orderedCount = counts.OrderByDescending(c => c.Value);
foreach (var count in orderedCount)
{
Console.WriteLine("{0} : {1}", count.Key, count.Value);
}
Console.ReadLine();
}
private static List<string> Combine(string[] arr1, string[] arr2, string[] arr3, string[] arr4, string[] arr5, string[] arr6)
{
List<string> combined = new List<string>();
combined.AddRange(arr1);
combined.AddRange(arr2);
combined.AddRange(arr3);
combined.AddRange(arr4);
combined.AddRange(arr5);
combined.AddRange(arr6);
return combined;
}
Outputs: A : 4, B : 4, D : 4, C : 3
I have 2 different collections.
pseudo code:
// index string by int : Dictionary<int, string>
index = { 0, "a" }, { 1, "b" }, { 2, "c" }
// data : Dictionary<string, List<Data>>
data = {"a", { "data00", "data01"..}},
{"b", {"data20", "data21", "data22"...}},
{"c", {"data4",...}}...
I want project int index to data string value and
var result = data.SelectMany ... new { IntIndex, DataValue }
I need to flatten lists into one sequence and pair the Data values with int index using string index.
I have slightly update types and values (your Dictionary contains duplicated keys and index wasn't specified) but it shouldn't be a problem. You may modify function easily for your data types.
var index = new List<Tuple<int, string>> {Tuple.Create(0, "a"), Tuple.Create(1, "b")};
var data = new Dictionary<string, IEnumerable<string>>()
{
{"a", new[] {"data00", "data01"}},
{"b", new[] {"data20", "data21", "data22"}},
{"c", new[] {"data4"}}
};
var result = index
.Join(data, x => x.Item2, y => y.Key, (x,y) => new KeyValuePair<int, IEnumerable<string>>(x.Item1, y.Value))
.SelectMany(x => x.Value, (x, y) => new KeyValuePair<int, string>(x.Key, y));
Assuming your duplication of keys was accidental, you could try this
Dictionary<int, List<Data>> intData = new Dictionary<int, List<Data>>();
foreach (var iVal in index)
{
List<Data> tmpList = new List<Data>();
if (data.TryGetValue(iVal.Value, out tmpList))
{
intData.Add(iVal.Key, tmpList);
}
}
If you can have duplicate keys then a dictionary is not the right structure.
var index = new List<Tuple<int, string>> {Tuple.Create(0, "a"), Tuple.Create(1, "b")};
var data = new Dictionary<string, IEnumerable<string>>()
{
{"a", new[] {"data00", "data01"}},
{"b", new[] {"data20", "data21", "data22"}},
{"c", new[] {"data4"}}
};
var res =
(from i in index
join d in data on i.Item2 equals d.Key
select new {Key = i.Item1, Value = d.Value})
.SelectMany(x => x.Value, (x, v) => new {x.Key, Value = v});
I am trying to create a dictionary from 2 lists where one list contains keys and one list contains values. I can do it using for loop but I am trying to find if there is a way of doing it using LINQ.
Sample code will be helpfull. Thanks!!!!
In .NET4 you could use the built-in Zip method to merge the two sequences, followed by a ToDictionary call:
var keys = new List<int> { 1, 2, 3 };
var values = new List<string> { "one", "two", "three" };
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
.ToDictionary(x => x.Key, x => x.Value);
List<string> keys = new List<string>();
List<string> values = new List<string>();
Dictionary<string, string> dict = keys.ToDictionary(x => x, x => values[keys.IndexOf(x)]);
This of course assumes that the length of each list is the same and that the keys are unique.
UPDATE: This answer is far more efficient and should be used for lists of non-trivial size.
You can include the index in a Select expression to make this efficient:
var a = new List<string>() { "A", "B", "C" };
var b = new List<string>() { "1", "2", "3" };
var c = a.Select((x, i) => new {key = x, value = b[i]}).ToDictionary(e => e.key, e => e.value );
foreach (var d in c)
Console.WriteLine(d.Key + " = " + d.Value);
Console.ReadKey();
var dic = keys.Zip(values, (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
You can use this code and working perfectly.
C# Code:
var keys = new List<string> { "Kalu", "Kishan", "Gourav" };
var values = new List<string> { "Singh", "Paneri", "Jain" };
Dictionary<string, string> dictionary = new Dictionary<string, string>();
for (int i = 0; i < keys.Count; i++)
{
dictionary.Add(keys[i].ToString(), values[i].ToString());
}
foreach (var data in dictionary)
{
Console.WriteLine("{0} {1}", data.Key, data.Value);
}
Console.ReadLine();
Output Screen: