Search Unity

Noob Question, Debug.Log a List

Discussion in 'Scripting' started by Mortalanimal, Oct 16, 2019.

  1. Mortalanimal

    Mortalanimal

    Joined:
    Jun 7, 2014
    Posts:
    568
    Hi, I have this List and I want to know whats stored in it. How do I go about Debug.Loging this?

    Thank you in Advance

    On another note, If I have a Unit that is included in a list, and that unit (game object) is destroyed, does it remain in the List?

    Code (CSharp):
    1. public static List<Unit> SelectedUnitList = new List<Unit>();
     
    ChunkyCurd likes this.
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    Answer 1: just make a foreach loop and print each bit:

    Code (csharp):
    1. foreach( var x in myList) {
    2.  Debug.Log( x.ToString());
    3. }
    Or use a for (i = 0; i < myList.Count; i++) if you want to know the index of each item for printing purposes.

    Answer 2: yes: Destroy is internal, but the C# references will remain. However, Unity overloads the equality operator so the objects will appear to be null for most intents and purposes. Some people love this feature, some people hate this feature.

    If you have a list and want to prune all the destroyed objects out of it, this works handily:

    Code (csharp):
    1. myList.RemoveAll( x => !x);
    It's giving a "when not true" predicate to the RemoveAll method. Don't do this every frame obviously. :)
     
  3. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    If you want to see the entire contents of a list, you can either use a separate log line for each item, or you can concatenate all the items into a single string and then log that.
    Code (CSharp):
    1. // Log each element one at a time
    2. foreach (var item in myList)
    3. {
    4.     Debug.Log(item.ToString());
    5. }
    6.  
    7. // Concatenate all items into a single string
    8. // NOTE:  If the List is long, this would be more efficient with a
    9. // StringBuilder
    10. string result = "List contents: ";
    11. foreach (var item in myList)
    12. {
    13.     result += item.ToString() + ", ";
    14. }
    15. Debug.Log(result);
    Destroyed objects remain in a list. Unity GameObjects, MonoBehaviours, etc. will compare as equal to null when they have been destroyed.
     
  4. Mortalanimal

    Mortalanimal

    Joined:
    Jun 7, 2014
    Posts:
    568
    Kurt-Dekker likes this.
  5. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,330
    Here are some best practices I follow when it comes to printing lists of objects:

    1. If your list might contain null targets, you should do null checks inside your for-loop to prevent null reference exceptions when logging the contents. If you want to be perfectly safe from exceptions, you should also null check the list itself.

    2. UnityEngine.Object (that includes GameObjects) have special logic for determining when they are null. Because of this you should always have separate methods for printing lists of UnityEngine.Objects, instead of using a more general implemention (like printing an IList). Otherwise errors can occur when you call ToString for an element even after doing a null check and other weird things.

    3. I always print all targets using just one Debug.Log call, because otherwise huge slowdowns can occur when you print the contents of large lists. It's also then much easier to then copy the printed list to the clipboard.

    4. Using StringBuilder is better than simple concatenation when it comes to lists, because it reduces the amount of garbage generated. Of course if you only do this rarely and from editor code it doesn't matter.

    5. You need to figure out what it is exactly you want to print when it comes to an UnityEngine.Object element. For components, is the type name enough, or do you also want the name? With GameObjects, is the name enough, or do you need the whole hierarchy path? What is useful can depend a lot on the situation. Say, if you're printing the contents of a large list of transforms, just printing the type name probably isn't very useful.


    Here is an example implementation:

    Code (CSharp):
    1. using System.Text;
    2. using System.Collections.Generic;
    3. using JetBrains.Annotations;
    4. using Object = UnityEngine.Object;
    5.  
    6. public static class StringUtils
    7. {
    8.     public static string ToString([CanBeNull]List<Object> list, string delimiter = "\n")
    9.     {
    10.         if(list == null)
    11.         {
    12.             return "null";
    13.         }
    14.  
    15.         int lastIndex = list.Count - 1;
    16.         if(lastIndex == -1)
    17.         {
    18.             return "{}";
    19.         }
    20.  
    21.         var builder = new StringBuilder(500);
    22.         builder.Append('{');
    23.         for(int n = 0; n < lastIndex; n++)
    24.         {
    25.             Append(list[n], builder);
    26.             builder.Append(delimiter);
    27.         }
    28.         Append(list[lastIndex], builder);
    29.         builder.Append('}');
    30.          
    31.         return builder.ToString();
    32.     }
    33.  
    34.     public static void Append(Object target, StringBuilder toBuilder)
    35.     {
    36.         if(target == null)
    37.         {
    38.             toBuilder.Append("null");
    39.         }
    40.         else
    41.         {
    42.             toBuilder.Append("\"");
    43.             toBuilder.Append(target.name);
    44.             toBuilder.Append("\" (");
    45.             toBuilder.Append(target.GetType().Name);
    46.             toBuilder.Append(")");
    47.         }
    48.     }
    49. }
     
    Last edited: Apr 6, 2020
    peterroth124 and khaled24 like this.
  6. Mortalanimal

    Mortalanimal

    Joined:
    Jun 7, 2014
    Posts:
    568
    @SisusCo, Thanks bro, do you think if I add a script that removed my Object from all Lists before its destroyed my solve this issue?
     
  7. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,330
    @Mortalanimal Yep, that sounds like it would solve your issues.
     
  8. Mortalanimal

    Mortalanimal

    Joined:
    Jun 7, 2014
    Posts:
    568
    What if, I want to know total Number of units in my list as an int?

    I think I done it, Via trial and error lol


    Code (CSharp):
    1.  int count = SelectionManager.ctrlGroup1.Count;                    
    2.                     SelectionManager.ctrlGroup1.Remove(myUnit);
    3.                     Debug.Log("Removed");
    4.                     Debug.Log(count);
     
    Last edited: Oct 19, 2019
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
  10. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    That is a question that deserves its own thread with code included.
    We can't make any suggestions when you don't post your own code of what you have that doesn't meet up your expectations.
    Sure I could do a shot in the dark and talk about using the System.Path API and suggest you'd do a string.Split to get all folder names as an array. But whether it is the solution you require is the question. Without your code it is hard to suggest something.
     
  11. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    What is on line 69 of this script?
    Assets/My Scripts/ListFolder.cs:69
     
  12. Xepherys

    Xepherys

    Joined:
    Sep 9, 2012
    Posts:
    204
    I tend to make methods for this sort of thing if I know i plan to do it more than once. Something like:

    Code (csharp):
    1.  
    2. public void Blah()
    3.         {
    4.             List<Vector3> vList = new List<Vector3>();
    5.  
    6.             for (int i = 0; i < 10; i++)
    7.             {
    8.                 vList.Add(new Vector3.Random());
    9.             }
    10.  
    11.             Debug.Log(ListToString(vList));
    12.         }
    13.  
    14.         public string ListToString<T>(List<T> list)
    15.         {
    16.             StringBuilder sb = new StringBuilder();
    17.             foreach (T v in list)
    18.             {
    19.                 sb.Append(v.ToString() + Environment.NewLine);
    20.             }
    21.  
    22.             return sb.ToString();
    23.         }
    24.  
    And in the event a datatype doesn't provide a useful
    .ToString()
    , you can often override it with something more useful.
     
  13. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    Debug.Log($"List Contents:\n{string.Join("\n", list)}");
    could do the trick as well. As long as the type in the list has a proper .ToString() implementation
     
    Snowirbis and brian_mcgee_nb like this.
  14. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    Every question should be a new thread. Put a link to the old thread if it is truly relevant, otherwise do not.

    How to report problems productively in the Unity3D forums:

    http://plbm.com/?p=220

    Help us to help you.
     
  15. Apaghost

    Apaghost

    Joined:
    Oct 8, 2019
    Posts:
    3
    I'm sorry guys, you were right, i didn't post in the right thread, and i realized it just now. i feel so noob ! I'm going to clean that mistake asap.
     
    Last edited: Aug 21, 2020
  16. marcospgp

    marcospgp

    Joined:
    Jun 11, 2018
    Posts:
    194
    When do we get something like what we all have in our browser right now?

    Press F12 and type
    [1, 2, 3]
    in the console.

    Why do we have to write a loop to inspect a list in the current year?
     
  17. Hikiko66

    Hikiko66

    Joined:
    May 5, 2013
    Posts:
    1,304
    Well, if you want to print a list out, then I assume you're debugging it, in which case, you don't have to print it out.

    You can just set a breakpoint in the script using that list and mouse over it in visual studio, and you can also use the immediate window console to query and modify the list to test changes at runtime. You don't have to modify your code.
     
    Last edited: May 20, 2022
    MaskedMouse likes this.
  18. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,001
    Because inside a browser you have a dynamic scripting language at hand (javascript). In almost all compiled languages you don't get such a complex behaviour out of the box. Most dynamic scripting languages have such features.

    However you can convert a list or array to a string in a single line by using a linq method. To use any of those you have to have
    using System.Linq;
    at the top. now you can simply do

    string.Join and Select
    Code (CSharp):
    1. string text = string.Join(", ",list.Select(v=>v.ToString()));
    Aggregate with StringBuilder
    Code (CSharp):
    1. string text = list.Aggregate(new System.Text.StringBuilder(),(b,v)=>b.Append(v).Append(", ")).ToString().TrimEnd(' ',',');
    Aggregate (less performant for large lists / arrays)
    Code (CSharp):
    1. string text = list.Aggregate("",(a,v)=>a + ", " + v.ToString()).TrimStart(' ',',');
    Select and Aggregate (less performant for large lists / arrays)
    Code (CSharp):
    1. string text = list.Select(v=>v.ToString()).Aggregate((a,b)=>a + ", " + b);
     
    TomTrottel and Joaquin-VtLab like this.
  19. Gigabitten_Gaming

    Gigabitten_Gaming

    Joined:
    Jul 10, 2017
    Posts:
    32
    I'm new to Unity but have coded in other disciplines for a long time.

    In most other languages, it is usually trivially easy and a very small amount of code to convert an array into a JSON string which you can then debug. For example, in javascript, it's simple
    Code (JavaScript):
    1. JSON.stringify(myArray)
    If one can do this in C# that would probably be the fastest approach, no?
     
  20. Nad_B

    Nad_B

    Joined:
    Aug 1, 2021
    Posts:
    730
    Unity's built in JSON serializer does not support naked arrays (arrays without a single parent object) which is a shame. So you could either use Newtonsoft.Json (probably already referenced in your project since it's used in a lot of tools/assets) like this:
    Code (CSharp):
    1. Debug.Log(JsonConvert.SerializeObject(yourArray));
    Or you could create a helper that wraps your array into a serializable class and serialize it if you want to use the built in JSON serializer (code from https://stackoverflow.com/a/38396727)


    Code (CSharp):
    1. public class JsonHelper
    2. {
    3.     public static string ToJson<T>(T[] array)
    4.     {
    5.         var wrapper = new Wrapper<T>();
    6.         wrapper.Items = array;
    7.  
    8.         return UnityEngine.JsonUtility.ToJson(wrapper);
    9.     }
    10.  
    11.     [System.Serializable]
    12.     private class Wrapper<T>
    13.     {
    14.         public T[] Items;
    15.     }
    16. }
    Usage:
    Code (CSharp):
    1. Debug.Log(JsonHelper.ToJson(yourArray));