Go to Top

LINQ is an incredibly powerful tool when working with lists and arrays of objects. In this tutorial you will get an introduction of how to use it to solve common problems.

Motivation

You should read this article if:

  • You'd like a way to find all of the transforms with GameObject.FindGameObjectsWithTag() rather than the game objects themselves
  • You need to manipulate, sort and change the types of lists and arrays but always seem to write too much code
  • You've heard of LINQ but don't know how it works
  • You don't believe me when I say I can write a single line of code that returns all of the materials on the renderers on the five tagged objects nearest to a target! (or anything else seemingly as complex)
While I'm setting puzzles and you are worrying about writing a single line of code to do all that - for light relief why don't you also consider this question? Can you think of the English sentence which is grammatically correct and contains the word "and" five times in a row?  The totally irrelevant answer is at the end (no peeking)

Introduction

LINQ stands for Language Integrated Native Query and in .NET it's a way of writing things that look like database queries but work on objects.  It's very powerful and, when you get your head around it, very easy to use.

LINQ is can be slower than writing out all of the for next loops. It can be up to half as fast and so should be considered carefully in Update functions where it occurs every frame.  It's great for caching, it's great for processing.

In C# you can actually write things that look very like SQL statements:

var transformsWithTag = from go in GameObject.FindGameObjectsWithTag("YourTag") 
     where Vector3.Distance(go.transform.position - transform.position) < 100
     select go.transform;

But we'll be using the function based approach with is much more powerful and, I believe more readable.

The Power Of Enumerations

So Linq is a way of making an enumerator that enumerates an existing enumerable collection but changes it as it goes.  So you can use Linq on anything that is an array, a list, a dictionary - basically a collection of any kind.  Note that as a Linq function returns an enumeration - you can chain them together - and therein lies the power.

Linq lets you write a series of functions that manipulate the  collection. Supplying your own code in the form of anonymous functions.

Let's get into it.

Enabling LINQ

To use Linq you need to add an import or a using directive to your source code.

//C#
using System.Collections.Generic; //Always a good idea
using System.Linq;

//JavaScript
import System.Linq;

That's it!

A First Linq Statement

So let's do something simple - lets make an array of all of the transforms associated with game objects returned by GameObject.FindGameObjectsWithTag.

  • Find all the game objects
  • Select the transform from the game object
  • Turn the result into an array
That looks like this:
//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(go => go.transform)
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(function (go) go.transform)
    .ToArray();

So notice how we use an anonymous function to do the selection work we want.   Select says take the current thing in the enumeration (at this point a game object), call it whatever (in our case go) and then return the result of some calculation (in our case get the transform) and then ToArray runs the whole job and puts the results into an array.

In C# we use a Lambda function: go => go.transform, and in JavaScript we use an anonymous function: function(go) go.transform

We aren't limited to a single statement in that function either!  We can put { and } and type any amount of code we like... More on this later.

Performing a Query

Ok so the Q is Query - let's do something that asks a question.  Let's get all of the transforms for the objects within 10 world units of the current position.

//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(go => go.transform)
    .Where(t => Vector3.Distance(t.position - transform.position) < 10
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(function (go) go.transform)
    .Where(function (t) Vector3.Distance(t.position - transform.position) < 10)
    .ToArray();

So we've put our Where function after the select - so the enumeration is already an enumeration of Transforms - our Where anonymous function must return true or false so we make a simple test of the distance from the transform in the enumeration to our current transform.

Performing a Sort

Ok, let's imagine that some of our objects will have a script attached called Danger and that this script contains a float variable with a value called dangerLevel.  Let's sort our list of transforms by this danger level (if it exits, otherwise let's say they really aren't dangerous at all).

//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(go => go.transform)
    .Where(t => Vector3.Distance(t.position - transform.position) < 10
    .OrderByDescending(t => {
       var danger = t.GetComponent<Danger>();
       return danger ? danger.dangerLevel : 0;
      })
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(function (go) go.transform)
    .Where(function (t) Vector3.Distance(t.position - transform.position) < 10)
    .OrderByDescending(function (t) {
          var danger = t.GetComponent(Danger);
          return danger ? danger.dangerLevel : 0;
         })
    .ToArray();

So we've added an OrderByDescending (sort them top to bottom).  We've also written a multi line function, because first we needed to get the component and then use one of its values.  You see how easy that was?  It's just like a normal function.

Right cool - but what about if we then also wanted them sorted by proximity - danger then proximity being the important factor?

//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(go => go.transform)
    .Where(t => Vector3.Distance(t.position - transform.position) < 10
    .OrderByDescending(t => {
       var danger = t.GetComponent<Danger>();
       return danger ? danger.dangerLevel : 0;
      })
    .ThenBy(t => Vector3.Distance(t.position, transform.position)
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .Select(function (go) go.transform)
    .Where(function (t) Vector3.Distance(t.position - transform.position) < 10)
    .OrderByDescending(function (t) {
          var danger = t.GetComponent(Danger);
          return danger ? danger.dangerLevel : 0;
         })
    .ThenBy(function (t) Vector3.Distance(t.position, transform.position)
    .ToArray();

You can chain as many ThenBy and ThenByDescending as you like (note we should have used sqrMagnitude for performance reasons as the order is the same, but for clarity, we'll keep it as Vector3.Distance).

Drilling Down

You can also use LINQ to drill into the contents of a collection which is the member of an item in the list.

In our previous example we were returning the transform of all of the items found by tag - what if for some reason we wanted all of their renderers, in them and all of their descendants in the hierarchy?  That's where SelectMany comes in:

//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .SelectMany(go => go.GetComponentsInChildren<Renderer>())
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .SelectMany(function (go) go.GetComponentsInChildren(Renderer))
    .ToArray();

SelectMany makes the collection a concatenation of all of the collections returned by it, so after our SelectMany the array will contain all of the renderers on all of the items.

Linq inside Linq

Let's say that last example wasn't what we wanted - let's say we wanted the renderer on the object and only its immediate children - still simple, but we will use a Linq statement inside a Linq statement...

//C#

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .SelectMany(go => go.transform.Cast<Transform>()
       .Select(t=>t.renderer)
       .Concat(new [] { go.renderer })
       .Where(r=>r!=null)
     )
    .ToArray();

//Javascript

var transformArray = GameObject.FindGameObjectsWithTag("MyTag")
    .SelectMany(function (go) go.transform.Cast.<Transform>()
         .Select(function (t) t.renderer)
         .Concat([go.renderer])
         .Where(function (r) r != null)
     )
    .ToArray();

So we get the transform and cast it to be a collection of Transforms (it's a weirdness in Unity that it isn't that by default, as it's capable of getting all of its children).  Then we select the renderer from each child and concatenate that enumeration with an array containing the renderer of the parent, then we only allow through the ones that aren't null.

Job done.

Finding the Closest Object

Ok so there are a couple of ways we could find the closest object - here's the inefficient one:

//C#

var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
    .OrderBy(go => Vector3.Distance(go.transform.position, transform.position)
    .FirstOrDefault();

//Javascript

var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
    .OrderBy(function (go) Vector3.Distance(go.transform.position, transform.position))
    .FirstOrDefault();

So here we order the collections by the distance and we get the FirstOrDefault one.  FirstOrDefault will return null if there are no objects in the list - you can also use First but it will throw an exception if there aren't any items.

The problem with this is that we had to sort the list - that takes time, especially when we only want the closest object.

We can use the generic Aggregate function to mean we don't have to do that:

var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
   .Aggregate((current, next)=> Vector3.Distance(current.transform.position, transform.position) < Vector3.Distance(next.transform.position, transform.position) 
      ? current : next);

//Javascript

var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
     .Aggregate(function(current, next) Vector3.Distance(current.transform.position, transform.position) < Vector3.Distance(next.transform.position, transform.position) 
        ? current : next);

So this only runs through the list once, each step it works out the closest of the two candidate items and returns that.  It only passes the list once.  In C# you can optimise this a bit using anonymous classes too (doesn't work anywhere near as well in JavaScript so I'll leave it out).

//Optimal C#
var currentPos = transform.position;    //Cache it - it's expensive!
var closestGameObject = GameObject.FindGameObjectsWithTag("MyTag")
   .Select( go => new { go = go, position  = go.transform.position })
   .Aggregate((current, next)=> 
      (current.position - currentPosition).sqrMagnitude <
      (next.position - currentPosition).sqrMagnitude
      ? current : next).go;

So in our first Select we just made up a new class with two members - go and position - that's what's in the enumeration now.  Then we use that cached position to work out which is closer (using the optimal sqrMagnitude method to avoid an expensive square root).  At the end we have the closest object, but it's now our anonymous class, so we convert it to a game object by taking the go variable.

Lists and Dictionaries

So we aren't limited to making arrays - we can make a list using .ToList() and we can make a Dictionary too!

Dictionaries clearly have a key so let's see how we make that...  Let's find all of the game objects in our scene with a tag and put them into a dictionary based on that tag:

//C#

var lookupByTag = GameObject.FindObjectsOfType(typeof(GameObject))
     .Cast<GameObject>()
     .Where(go=>!string.IsNullOrEmpty(go.tag))
     .ToLookup(go => go.tag);

//JavaScript

var lookupByTag = GameObject.FindObjectsOfType(GameObject)
    .Cast.<GameObject>()
    .Where(function (go) go.tag != "")
    .ToLookup( function (go) go.tag );

This creates a special sort of dictionary that we can access a list of game objects from with lookupByTag["SomeTag"]

We could create an ordinary dictionary perhaps of GameObject to distance (plug in a game object and it returns its distance from the target point).  This would be very fast if we continually needed to know that due to some calculation that ran on many game objects.

//C#

var objectToDistance = GameObject.FindObjectsOfType(typeof(GameObject))
     .Cast<GameObject>()
     .ToDictionary(go=>go, go=>Vector3.Distance(go.transform.position, transform.position));

//Get the distance of a GO later:

var distance = objectToDistance[someGameObject];

Notice the first parameter in ToDictionary is a key and the second is what we want in the dictionary values.  So this is returning a Dictionary<GameObject, float>.

Conclusion

So hopefully this first introduction has shown you some handy tips for using Linq to get going with list manipulations.  We'll explore more next time, because more there is!

Answers

You don't believe me when I say I can write a single line of code that returns all of the materials on the renderers on the five tagged objects nearest to a target!

var materials = GameObject.FindGameObjectsWithTag("sometag").Select(r=>r.renderer).Where(r=>r!=null).OrderBy(r=>(r.transform.position - transform.position).sqrMagnitude).Take(5).SelectMany(r=>r.materials).ToList();

foreach(var m in materials) m.color = Color.red;

Can you think of the English sentence which is grammatically correct and contains the word "and" five times in a row?

There is a man painting a pub sign for the "Pig and Whistle" pub.  The landlord walks outside, looks at the sign and says to the painter: "You need to leave more space between the Pig and 'and' and 'and' and Whistle."

, , , ,

24 Responses to "Linq #1 – It’s Time To Think Linq"

  • yc
    January 1, 2013 - 9:38 pm Reply

    It’s kind of neat that you can get LINQ to work on Android by using this sledgehammer known as Unity… :D

  • CrippleAlex
    January 22, 2013 - 4:09 pm Reply

    It’s kind of neat that you can get LINQ to work on Android by using this sledgehammer known as Unity…

    It is not unity, it is Mono :)

    Unity uses Mono ^^

  • Dizzy
    January 29, 2013 - 10:23 pm Reply

    Dude, I have one word: Kabooooooooooooooooom!!!!
    Mind. Is. Blown!!!

    • fafase
      April 7, 2013 - 1:44 pm Reply

      You did add the using System.Linq; at the top?

  • prototype7
    April 11, 2013 - 3:01 pm Reply

    Awesome, i like you fafase. Thanks for the tutorial

    • whydoidoit
      April 11, 2013 - 8:06 pm Reply

      Actually I wrote this one ;)

  • prototype7
    April 12, 2013 - 7:02 am Reply

    Hi Mike,

    Vector3.Distance(go.transform.position – transform.position)
    for some example above should be
    Vector3.Distance(go.transform.position, transform.position)
    isn’t it ?

  • Tom
    October 8, 2013 - 6:13 pm Reply

    Hi,

    What about Linq performance? In which cases it’s recommended not to use Linq?

    Thanks,

    Tom

    • whydoidoit
      October 9, 2013 - 1:06 pm Reply

      I would not suggest using it every update because of the memory it uses. I’m about to release a faster and memory efficient version of Linq for Unity – with the standard version it’s Mono’s GC that is such a nightmare for anything that allocates memory.

      Linq sorting is faster than Array.Sort() with a predicate for instance.

      • Alejandro
        December 10, 2013 - 9:34 pm Reply

        ‘Faster and memory efficient’ version of Linq for Unity?
        Can you elaborate on this? :O…
        that would be awesome man, double curious on how are you doing this, from scratch?

  • MGB
    January 14, 2014 - 5:44 pm Reply

    Care should be taken when targeting iOS as some Linq functions will not work.

    • whydoidoit
      January 15, 2014 - 6:00 pm Reply

      Yep Sum doesn’t work – but fortunately Aggregate does.

  • Stephen
    March 9, 2014 - 4:07 pm Reply

    Hey, I can get the nearest object just fine, but unless it’s destroyed, it always shows up as the closest target, even when it’s not.

    Is there a way to clear the list and re-query the objects?

    Thanks!

    • Stephen
      March 9, 2014 - 4:13 pm Reply

      Nevermind, my goof. I wasn’t refreshing the player’s location.

  • Smooth_P
    April 2, 2014 - 1:42 am Reply

    Coming from an internet start-up background where productivity is king, I grew incredibly weary of purposely avoiding Linq and actually *choosing* to waste development time on writing and maintaining tons of convoluted loops.

    So I’ve just released Smooth.Slinq, a faster-than-Linq-with-more-features-and-allocation-free, Linq / Scala inspired enumeration API for Unity.

    Asset Store Link – https://www.assetstore.unity3d.com/#/content/16249

    Slinq Documentation – https://www.smooth-games.com/docs/smooth-slinq/
    Base Documentation (eg: Option, Tuple) – https://www.smooth-games.com/docs/smooth-base/

    Support Forums – https://www.smooth-games.com/forums/

    • whydoidoit
      April 2, 2014 - 3:20 am Reply

      Nice! I keep meaning to publish my non-allocating/faster version, glad someone has got around to making one available…

      • Smooth_P
        April 2, 2014 - 3:39 am Reply

        It certainly had to be done!

        Note that I prioritized predictability and flexibility of memory usage over raw throughput for this initial version and based my internal data structures on singly linked lists. C# Lists would certainly be faster, but also grow to consume more memory when using grouping operations with random input and wouldn’t support “zero-copy” reorderings.

        Plus, faster than LINQ with more features and no allocations seemed good enough for now. :)

  • Patm
    April 12, 2014 - 3:15 pm Reply

    Your C# examples are all JScript examples except the references at the top.

    • whydoidoit
      April 12, 2014 - 3:25 pm Reply

      No those examples are in c#. Perhaps you don’t know about var in c# which is vital for Linq.

  • angel
    May 30, 2014 - 11:50 pm Reply

    I had heard than unityscript has not lambdas but in your examples seems to be pretty similar to the javascript lambdas or even more similar to lua lambdas…it was added recently in unityscript? ..I’m just starting my learning about unity and I think I going to choose unityscript over c# (c# feels a bit verbose and is not feel good like a scripting language) but I need be sure than unityscript hasn’t any downside compared to c# (I can live with a not so good intellisense) and for me is vital the lambda support…

    How you compare unity script with c# beyond the differences in the syntax??…thanks and sorry for my english…as Celia Cruz said “sorry..my english is not good looking :D”

    • whydoidoit
      May 30, 2014 - 11:55 pm Reply

      Yes there is Lambda (or at least anonymous function support) in UnityScript. Personally I would recommend Csharp though – UnityScript can lack some documentation and does not support some of the extended functionality that you get with a complete language like csharp (extension methods, properties, casting + a few). The intellisense support is actually good in MonoDevelop for both languages.

      I feel that Unity script is more verbose than Csharp – except for the fact you have to declare a class for each script which is hidden from you in UnityScript and around the automatic starting of Coroutines. I’ve used both and both are good – but Csharp is the same Csharp everywhere and UnityScript is really just Unity.

Leave a Reply

%d bloggers like this: