Linq query to list in structure

I have a struct dictionary, where one member is a list containing various elements applicable to each element of the dictionary.

I would like to combine these elements with each element in order to filter them and / or group by element.

In SQL, I'm familiar with joining tables / queries to get multiple rows as desired, but I'm new to C # / Linq. Since the “column” may be an object / list already associated with the corresponding dictionary items, I wonder how I can use them to make the connection?

Here is a sample structure:

name   elements
item1  list: elementA
item2  list: elementA, elementB

I need a query that gives this result (count = 3)

name   elements
item1  elementA
item2  elementA
item2  elementB

As a result, grouping them like this:

   element    count
   ElementA   2
   ElementB   1

Here, my code begins to count dictionary entries.

    public struct MyStruct
    {
        public string name;
        public List<string> elements;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MyStruct myStruct = new MyStruct();
        Dictionary<String, MyStruct> dict = new Dictionary<string, MyStruct>();

        // Populate 2 items
        myStruct.name = "item1";
        myStruct.elements = new List<string>();
        myStruct.elements.Add("elementA");
        dict.Add(myStruct.name, myStruct);

        myStruct.name = "item2";
        myStruct.elements = new List<string>();
        myStruct.elements.Add("elementA");
        myStruct.elements.Add("elementB");
        dict.Add(myStruct.name, myStruct);


        var q = from t in dict
                select t;

        MessageBox.Show(q.Count().ToString()); // Returns 2
    }

: , . , ( item.name, ). , /, , . .ToDictionary, key = item.Name .

+5
6
var q = from t in dict
    from v in t.Value.elements
    select new { name = t.Key, element = v };

Enumerable.SelectMany. :

var q = dict.SelectMany(t => t.Value.elements.Select(v => new { name = t.Key, element = v }));

, t.Value.name , t.Key, .

, ?

-, , ; , , . , :

class NameElement
{
    public string name { get; set; }
    public string element { get; set; }
}
IEnumerable<NameElement> GetResults(Dictionary<string, MyStruct> dict)
{
    foreach (KeyValuePair<string, MyStruct> t in dict)
        foreach (string v in t.Value.elements)
            yield return new NameElement { name = t.Key, element = v };
}

(, )?

( fooobar.com/questions/3208/..., , :)

, NameElement. , . :

var q = GetResults(dict);

:

var q = GetResults(dict, (string1, string2) => new { name = string1, element = string2 });

- (string1, string2) => new { name = string1, element = string2 } , 2 , (string1, string2), , , new { name = string1, element = string2 }.

:

IEnumerable<T> GetResults<T>(
    IEnumerable<KeyValuePair<string, MyStruct>> pairs,
    Func<string, string, T> resultSelector)
{
    foreach (KeyValuePair<string, MyStruct> pair in pairs)
        foreach (string e in pair.Value.elements)
            yield return resultSelector.Invoke(t.Key, v);
}

T . , ( , #), , , : .

, T pair, T, v e, "element". , IEnumerable<KeyValuePair<string, MyStruct>>. , , . , dict pairs.

. foreach - T. ; Func<KeyValuePair<string, MyStruct>, T>. , , pair , Select resultSelector:

IEnumerable<T> GetResults<T>(
    IEnumerable<KeyValuePair<string, MyStruct>> pairs,
    Func<string, string, T> resultSelector)
{
    foreach (KeyValuePair<string, MyStruct> pair in pairs)
        foreach (T result in pair.Value.elements.Select(e => resultSelector.Invoke(pair.Key, e))
            yield return result;
}

:

IEnumerable<T> GetResults<T>(
    IEnumerable<KeyValuePair<string, MyStruct>> pairs,
    Func<KeyValuePair<string, MyStruct>, IEnumerable<T>> resultSelector)
{
    foreach (KeyValuePair<string, MyStruct> pair in pairs)
        foreach (T result in resultSelector.Invoke(pair))
            yield return result;
}

; , , , :

var q = GetResults(dict, pair => pair.Value.elements.Select(e => new { name = pair.Key, element = e }));

( ), KeyValuePair<string, MyStruct> TSource. :

T     -> TResult
pairs -> sourceSequence
pair  -> sourceElement

, , :

static IEnumerable<TResult> GetResults<TSource, TResult>(
    this IEnumerable<TSource> sourceSequence,
    Func<TSource, IEnumerable<TResult>> resultSelector)
{
    foreach (TSource sourceElement in sourceSequence)
        foreach (T result in resultSelector.Invoke(pair))
            yield return result;
}

: SelectMany! , - , , , .

MSDN: SelectMany " IEnumerable ."

+3

, .

var groups = dictionary
    .SelectMany(o => o.Value)
    .GroupBy(o => o);

foreach (var g in groups)
    Console.WriteLine(g.Key + ": " + g.Count());

:

Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>();
dictionary.Add("One", new string[] { "A" });
dictionary.Add("Two", new string[] {"A", "B" });
dictionary.Add("Three", new string[] { "A", "B" });

:

 A: 3
 B: 2
+1
/* Will return 
name   elements
item1  elementA
item2  elementA
item2  elementB 
*/
var res = dict
    .Values
    .SelectMany(m => m.elements.Select(e => new {m.name, element= e}))
    .ToArray();

/* Will return 
element    count
ElementA   2
ElementB   1 
*/
var res2 = res
    .GroupBy(r => r.element)
    .Select(g => new {element = g.Key, count = g.Count()})
    .ToArray();
+1

, .

Dictionary<String, string> dict2 = new Dictionary<string, string>();

 dict.foreach(item => item.elements.foreach(elem => dict2.Add(elem,item.name)));

, , , , . , , ,

0

You might want to start with a simpler collection of structures, but from your dictionary:

var q = from t in dict.Values  
            from el in t.Elements  
            group el by el into eNameGroup  
            select new { Name = eNameGroup.Key, Count = eNameGroup.Count() };

This returns:

Account Name
ElementA 2
ElementB 1

0
source

If you are going to group / rotate, this can be done more declaratively using LINQ grouping and avoiding dictionaries altogether:

void Main()
{
    var items = new MyStruct[] { 
        new MyStruct { name = "item1", elements = new List<string> { "elementA" }},
        new MyStruct { name = "item2", elements = new List<string> { "elementA", "elementB" }}};

    var groupedByElement =
        from item in items
        from element in item.elements
        group item by element;

    groupedByElement.Dump(); // items grouped by element value, (pivoted)

    var elementsWithCount =
        from gj in groupedByElement
        select new { element = gj.Key, count = gj.Count() };

    elementsWithCount.Dump();
    // element, count
    // elementA, 2
    // elementB, 1
}

public struct MyStruct
{
    public string name;
    public List<string> elements;
}
0
source

All Articles