TIANLUN ZHU
TIANLUN ZHU

Reputation: 351

Confused on c# lambda expression with Linq

I am trying to understand this part of code:

int[] triangles = mesh.triangles;
Color32[] colors = mesh.colors32;
IEnumerable<IGrouping<byte, int>> hierarchyMap = colors
        .Select((color, index) => new { color, index })
        .GroupBy(c => c.color.g, c => c.index);

IEnumerable<int> leafIndexes = hierarchyMap
        .Where(x => x.Key == 255)
        .SelectMany(x=>x);

Dictionary<int, HashSet<int>> faces = triangles
        .Select((vert, index) => new { vert, index })
        .GroupBy(g => g.index / 3, i => i.vert)
        .Where(g=> leafIndexes.Any(leaf=>g.Contains(leaf)))
        .ToDictionary(g=>g.Key, g=>new HashSet<int>(g));

These code look like magic for me. It seems like "c" represents each element in colors, and type Color32 indeed has a property called color. But in the last line, triangle is an array of type int, how can an int type have an property called vert? And both int and Color32 don't have property index.

I am so confused on these expressions, I only find some simple example of Lambda expression online. After reading the examples, I am still stuck on these codes.

Upvotes: 0

Views: 160

Answers (2)

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131384

As I said in the comments, this code is too "clever" for its own good, and very, very slow due to repeated iterations.

The first two queries are redundant, as in the end, leafIndexes contains just the indexes of color 255, or rather, 0x0000FF. Select((item,index)=>...) passes the item and its index to the lambda. Those two queries could be just :

var leafIndexes=colors.Select((color,idx)=>{color,idx})
                      .Where(color=>color=255)
                      .Select(pair=>pair.idx)
                      .ToList();

Or, using an iterator function:

IEnumerable<int> ColorIndexes(IEnumerable<Color32> colors,int color)
{
    int i=0;
    foreach(var c in colors)
    {
        if(c==color) yield return i;
        i++;
    }
}

... 

var leafIndexes=ColorIndexes(colors,255).ToList();

The final query tries to batch vertices in threes. An iterator method could also help here, but a better idea would be to use MoreLINQ's Batch operator :

int[] vertices= mesh.triangles;
var triangles=vertices.Batch(3);

After that, the query tries to find which "triangle" values are contained in the leafIndexes list. Each "triangle" is a list of numbers though. We could write:

var finalTriangles=triangles.Where(points=> points.Any(point=>leafIndexes.Contains(point));

Which tries to find if any of the triangle points is contained in the leaf indexes. Or we could use Enumerable.Intersect to see if the the two arrays have any values in common:

var finalTriangles=vertices.Batch(3)
                           .Where(points=> points.Intersect(leafIndexes).Any());

The final step in the query creates a dictionary with the matching "triangles" whose key is the "triangle" index. Again, this is a job for Select((item,index)) :

int[] vertices= mesh.triangles;
var finalTriangles=vertices.Batch(3)
                           .Select((triplet,idx)=>{ triplet=triplet.ToList(),
                                                    idx})
                           .Where(pair=> pair.triplet.Intersect(leafIndexes).Any())
                           .ToDictionary(pair=>pair.idx,
                                         pair=>pair.triplet);

The code uses ToList() to execute the enumerable just once and return a List. Without it, every timetripletorleafIndexes` was used, the query would execute again.

The one thing this code doesn't do is put the points into a HashSet. This class is meant for fast, set-based, in-place operations. If triplet was a HashSet, calling IntersectWith would modify it and leave only the numbers found in leafIndexes.

Upvotes: 1

derHugo
derHugo

Reputation: 90659

how can an int type have an property called vert? And both int and Color32 don't have property index.

You are confusing one thing here: These are no "properties" but iteration variables.

Linq basically almost all the time is a shortcut for a

foreach(var color in colors)

for example. It is totally up to you if you call it color or item or anything else!

Honestly I didn't know but apparently the index you can just include when you need it. It is the index of according element within the list/IEnumerable you are using Linq on.

So in GroupBy you could actually again call it color instead of c.

Also in the later vert is no property but simply is a name for the current item .. you could also use

Dictionary<int, HashSet<int>> faces = triangles
    .Select((v, i) => new { v, i })

and it would do exactly the same thing.

Upvotes: 0

Related Questions