I have a Dictionary<Key, <Quality,Item>> which is tracking the relationship between qualities and items.
Qualities are an object type, Items are an object type, elsewhere I have lists of valid Qualities and valid Items. Items have a fixed list of qualities, always more than one. Qualities can be held by any number of items, including 0, depending on the state of the program.
Currently, Item objects also track their own Qualities in a List as one of my failed strategies to solve this. I have no idea if this is helpful or not, it’s sure not helping me right now and will probably be ripped out if proved useless.
I already have a LINQ self-join that collects pairs of unique Items that share at least one Quality successfully.
var r = from KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_1
in QualitiesToItems
join KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_2
in QualitiesToItems
on virtQ2I_1.Value.Item1.name equals virtQ2I_2.Value.Item1.name
where (virtQ2I_1.Value.Item2.name != virtQ2I_2.Value.Item2.name)
select new List<Item>
{
virtQ2I_1.Value.Item2,
virtQ2I_2.Value.Item2
};
Afterwards I use another Dictionary to clean up the little burp where <ItemA, ItemB> are considered the same as <ItemB, ItemA>.
What Is Needed: A list of each triplet of unique Items that share at least one Quality with at least one other Item in the triplet. Big hairy complication: the third item in the triple can’t just share one of the existing shared Qualities; it must bring something new to the relationship.
And I need the results starting from a list of a few hundred Items quickly – my existing solution doesn’t meet this last requirement.
Example:
- ItemA is Furry, Blonde, Four-Legged, and Trained
- ItemB is Furry, Rowan, Six-Legged, and Trained
- ItemC is Feathered, Blue, Two-Legged, and Trained
-
ItemD is Scaled, Rowan, Slithers, and Untrained
-
ItemA and ItemB will already have been picked up as a valid pair,
sharing the qualities Furry and Trained. (B:D of course are another
valid pair, as are A:C and B:C) -
ItemA, ItemB, and ItemC do not make a valid triplet as A:B are
already Trained, and ItemC has nothing else in common with either
ItemA or ItemB; A:B:C has the same Qualities list as A:B and therefor
C is rejected as “supernumerary” or “redundant”. -
ItemA, ItemB, and ItemD make a valid triplet as ItemD forms a pair
around Rowan with ItemB. The result of A:B:D is Furry, Rowan, Trained…
and I need that combination of A:B:D to go in my list of returned results.
-
I’m having problems going from the way I’m getting my pairs to the way I need to get my triplets in a way that works on a few hundred items in reasonable time.
I thought I was very clever when I wrote a method to look for shared Qualities between two Items and used it in my new LINQ query, but the result is… quite slow when used on more than a score or so of items, and my computer is overpowered compared to some of the machines this will be running on.
var r = from KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_1
in QualitiesToItems
join KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_2
in QualitiesToItems
on virtQ2I_1.Value.Item1.name equals virtQ2I_2.Value.Item1.name
join KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_3
in QualitiesToItems
on virtQ2I_2.Value.Item1.name equals virtQ2I_3.Value.Item1.name
where (virtQ2I_1.Value.Item2.name != virtQ2I_2.Value.Item2.name &&
virtQ2I_1.Value.Item2.name != virtQ2I_3.Value.Item2.name &&
virtQ2I_2.Value.Item2.name != virtQ2I_3.Value.Item2.name &&
Item.SharedQualities(this, new Item[2] { virtQ2I_1.Value.Item2, virtQ2I_2.Value.Item2 }).Count !=
Item.SharedQualities(this, new Item[3] { virtQ2I_1.Value.Item2, virtQ2I_2.Value.Item2, virtQ2I_3.Value.Item2 }).Count)
select new List<Item>
{
virtQ2I_1.Value.Item2,
virtQ2I_2.Value.Item2,
virtQ2I_3.Value.Item2
};
So: this worked, but I don’t like it. Is there a way to replace my function calls (and new item arrays) mid query with something pure LINQ? There must be.
A little more head-bashing against the problem has provided a solution that does the worst of the heavy lifting in LINQ, and has much better performance than what I tried in the original post.
I can do more clean-up, and I can probably replace the foreach with another LINQ query but that dynamites the big roadblock and significantly improves performance.