Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Bug Queue never equals another queue

Discussion in 'Scripting' started by stubbly69, Aug 11, 2022.

  1. stubbly69

    stubbly69

    Joined:
    Feb 1, 2020
    Posts:
    3
    Relatively new to Unity, apologies if I'm missing something but I've tried many different ways to get a queue to equal another queue. No matter if I use == or .Equals it never works. I want to compare two string or char queues but even when I try int it fails. Example using int queue below:

    private Queue<int> q1 = new Queue<int>();
    private Queue<int> q2 = new Queue<int>();
    private Queue<int> q3 = new Queue<int>();

    // Start is called before the first frame update
    void Start()
    {

    q1.Enqueue(1);
    q2.Enqueue(1);
    q2.Enqueue(2);
    q3.Enqueue(1);

    if (q1.Equals(q2))
    Debug.Log("The queues q1 and q2 are equal.");
    else
    Debug.Log("The queues q1 and q2 are not equal." );

    if (q1.Equals(q3))
    Debug.Log("The queues q1 and q3 are equal.") ;
    else
    Debug.Log("The queues q1 and q3 are not equal." );
    }

    not equal is returned both times.

    Am I missing something or is there a bug?

    Thanks
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,148
    If you're trying to compare values, then you need to compare the value that you would pull from the queue, not the queue itself.

    The queues can't ever be equal. Queues are reference types, even if the values stored inside are value types.
    It's like having two boxes and asking if they are the same box, which they aren't. Vs pulling a number 1 from each box and asking if the number is the same value, which it is.

    Thus the difference between value types and ref types.
     
    MelvMay likes this.
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,620
    That's such a good analogy. I'll have to remember that one. :D
     
    arkano22, Brathnann and Bunny83 like this.
  4. matthewminer

    matthewminer

    Joined:
    Aug 29, 2005
    Posts:
    331
    @Brathnann is bang on. To compare two queues, you need to iterate through them in lockstep and compare their elements. Here's one way to do this:

    Code (CSharp):
    1. bool AreEqual(Queue<int> q1, Queue<int> q2)
    2. {
    3.     if (q1.Count != q2.Count)
    4.     {
    5.         return false;
    6.     }
    7.  
    8.     return Enumerable
    9.         .Zip(q1, q2, (a, b) => (a, b))
    10.         .All(pair => pair.a == pair.b);
    11. }
    (this uses Linq, so the usual caveats apply)
     
    DevDunk and Bunny83 like this.
  5. stubbly69

    stubbly69

    Joined:
    Feb 1, 2020
    Posts:
    3
    Thanks, clearly all the examples I've looked at are wrong !
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    I would suggest an extension method like this:

    Code (CSharp):
    1.  
    2.     using System.Collections.Generic;
    3.  
    4.     public static class IEnumerableExtensions
    5.     {
    6.         public static bool ContainsEqualElements<T>(this IEnumerable<T> aFirst, IEnumerable<T> aSecond)
    7.         {
    8.             return ContainsEqualElements(aFirst, aSecond, EqualityComparer<T>.Default);
    9.         }
    10.         public static bool ContainsEqualElements<T>(this IEnumerable<T> aFirst, IEnumerable<T> aSecond, IEqualityComparer<T> aComparer)
    11.         {
    12.             if (aFirst == null || aSecond == null || aComparer == null)
    13.                 throw new System.ArgumentNullException();
    14.             var I1 = aFirst.GetEnumerator();
    15.             var I2 = aSecond.GetEnumerator();
    16.             bool I1HasElements, I2HasElements;
    17.             do
    18.             {
    19.                 I1HasElements = I1.MoveNext();
    20.                 I2HasElements = I2.MoveNext();
    21.             }
    22.             while (I1HasElements && I2HasElements && aComparer.Equals(I1.Current, I2.Current));
    23.             if (I1HasElements || I2HasElements)
    24.                 return false;
    25.             return true;
    26.         }
    27.         public static bool ContainsEqualElements<T>(this IEnumerable<T> aFirst, IEnumerable<T> aSecond, System.Func<T,T,bool> aComparison)
    28.         {
    29.             if (aFirst == null || aSecond == null || aComparison == null)
    30.                 throw new System.ArgumentNullException();
    31.             var I1 = aFirst.GetEnumerator();
    32.             var I2 = aSecond.GetEnumerator();
    33.             bool I1HasElements, I2HasElements;
    34.             do
    35.             {
    36.                 I1HasElements = I1.MoveNext();
    37.                 I2HasElements = I2.MoveNext();
    38.             }
    39.             while (I1HasElements && I2HasElements && aComparison(I1.Current, I2.Current));
    40.             if (I1HasElements || I2HasElements)
    41.                 return false;
    42.             return true;
    43.         }
    44.     }
    45.  
    It can compare any two enumerables and returns true if and only if they have the same number of elements in the same order. So it works on arrays, Lists or Queues.

    Just to make this clear: If any of the two queues have more elements than the other, it will return false. So they have to contain the same number of elements and the sequence has to be the same. Optionally you can provide your own comparer or comparison callback. So it can be easily adapted to work with custom types which may require special comparison handling.

    With this static class in your project you can simply do

    Code (CSharp):
    1. if (q1.ContainsEqualElements(q2))
    Since everything is based on IEnumerable you can also check an array against a List or a List against a queue. It would always work as long as the two enumerables produces the same sequence of elements.

    Code (CSharp):
    1.  
    2.     IEnumerable<int> MySequence()
    3.     {
    4.         yield return 1;
    5.         Debug.Log("Matched first");
    6.         yield return 4;
    7.         Debug.Log("Matched first and second");
    8.         yield return 7;
    9.         Debug.Log("Matched first 3 elements");
    10.         yield return 2;
    11.         Debug.Log("All matched");
    12.     }
    13.    
    14.     // [ ... ]
    15.    
    16.     if (q1.ContainsEqualElements(MySequence()))
    This would give you feedback how far the comparison went :) Note that this is not a Unity coroutine but just an ordinary generator function. Unity is just using those to implement coroutines. This is what they are actually made for. They "generate" values, that's why they are called a generator.
     
    Brathnann likes this.
  7. stubbly69

    stubbly69

    Joined:
    Feb 1, 2020
    Posts:
    3
    Thanks for all the tips I have a working solution now.
     
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,204
    That's just a hand-rolled Enumerable.SequenceEqual, is it not?

    So really the solution to OP's problem is to replace:
    Code (csharp):
    1. if (q1.Equals(q2))
    with
    Code (csharp):
    1. if (q1.SequenceEqual(q2))
    And remember to import Linq.
     
    matthewminer, stubbly69 and Bunny83 like this.
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    Yes, you're right :) I barely use Linq, especially since it's always a black box and you don't know how it's implemented. But yes, in this case it's probably fine. Also the extension name is much better :D