Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Grab 5 Unique Items from a List

Discussion in 'Scripting' started by Cyrussphere, Feb 22, 2017.

  1. Cyrussphere

    Cyrussphere

    Joined:
    Jul 1, 2016
    Posts:
    129
    Hey all,

    Going in circles on this one so I figured Id ask here to see if anyone had a simple solution to this. I have a master class list of quests, I would like to get 5 random unique quests from this list and add them into a new list without ever removing them from the master list. The below code grabs 5 of the quests but they wont always be unique. I tried to make a copy of the list using
    List<Quest> tempList = new List<Quest>(availMissions) but since its a Class list it makes only instances of the master list so If I remove them from the tempList it also removes them from the master list :(

    I am sure I am missing something simple that I just can't seem to find on google, so hoping someone knows a better solution.

    Code (CSharp):
    1.  
    2. public List<Quest> availMissions = new List<Quest>();
    3.  
    4. void LoadNewMissions()
    5.     {
    6.      
    7.         Debug.Log("Loading new missions");    
    8.         //Get 5 more quests
    9.         Quest newQuest1 = availMissions[Random.Range(0, availMissions.Count - 1)];
    10.         Quest newQuest2 = availMissions[Random.Range(0, availMissions.Count - 1)];
    11.         Quest newQuest3 = availMissions[Random.Range(0, availMissions.Count - 1)];
    12.         Quest newQuest4 = availMissions[Random.Range(0, availMissions.Count - 1)];
    13.         Quest newQuest5 = availMissions[Random.Range(0, availMissions.Count - 1)];
    14.         //Add 5 quests to a new list
    15.         stationMissions.Add(newQuest1);
    16.         stationMissions.Add(newQuest2);
    17.         stationMissions.Add(newQuest3);
    18.         stationMissions.Add(newQuest4);
    19.         stationMissions.Add(newQuest5);
    20.     }
    21.  
    Thanks!
     
  2. shashank1981

    shashank1981

    Joined:
    Feb 20, 2017
    Posts:
    24
    What you could try out is 'cloning' your master list so that tempList is a duplicate and then in the duplicate you can easily remove items. There should be several examples around the net that show cloning a list.
     
  3. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    I think this is what you're after. I change List<quest> to a List<string> so I could test it. change it back to quest and it should work

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class QuestLists : MonoBehaviour {
    6.  
    7.     public List<string> availMissions = new List<string>(); //list from gamemaster?
    8.     public List<string> stationMissions = new List<string>();
    9.  
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.         //add dummy data for testing
    14.         availMissions.Add("string0");
    15.         availMissions.Add("string1");
    16.         availMissions.Add("string2");
    17.         availMissions.Add("string3");
    18.         availMissions.Add("string4");
    19.         availMissions.Add("string5");
    20.         availMissions.Add("string6");
    21.         availMissions.Add("string7");
    22.         availMissions.Add("string8");
    23.         availMissions.Add("string9");
    24.  
    25.         LoadNewMissions();
    26.  
    27.     }
    28.  
    29.  
    30.     void LoadNewMissions()
    31.     {
    32.         //temp list
    33.         List<string> tempAvailMissions = new List<string>();
    34.  
    35.         //add all elements from gamemast list to the temp list
    36.         for (int i = 0; i < availMissions.Count; i++)
    37.         {
    38.             tempAvailMissions.Add(availMissions[i]);
    39.         }
    40.  
    41.         //pick 5 from the temp list, store them in stationMissions list
    42.         for (int i = 0; i < 5; i++)
    43.         {
    44.             int rand = Random.Range(0, tempAvailMissions.Count - 1);
    45.             stationMissions.Add(tempAvailMissions[rand]);
    46.             tempAvailMissions.RemoveAt(rand);
    47.         }
    48.      
    49.     }
    50. }
    51.  
     
    Cyrussphere likes this.
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,514
    You can use linq and a simple shuffle like so:

    Here's the shuffle method, I took this from my ArrayUtil:
    https://github.com/lordofduct/space...blob/master/SpacepuppyBase/Utils/ArrayUtil.cs
    Code (csharp):
    1.  
    2.     public static IEnumerable<T> Shuffled<T>(this IEnumerable<T> coll)
    3.     {
    4.         if (coll == null) throw new System.ArgumentNullException("coll");
    5.  
    6.         IList<T> buffer = coll.ToList();
    7.         int j;
    8.         for (int i = 0; i < buffer.Count; i++)
    9.         {
    10.             j = Random.Range(i, buffer.Count);
    11.             yield return buffer[j];
    12.             buffer[j] = buffer[i];
    13.         }
    14.     }
    15.  
    then you can just say:
    Code (csharp):
    1.  
    2. using System.Linq;
    3.  
    4. //...
    5.  
    6. var missions = availMissions.Shuffled().Take(5);
    7.  
     
    Ryiah and Cyrussphere like this.
  5. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    This is not true, it works.

    The problem that you describe would occur if you did this:
    Code (csharp):
    1. List<Quest> copiedReference = AvailableQuests;
    However, if you create a shallow copy:
    Code (csharp):
    1. List<Quest> shallowCopy = new List<Quest>(AvailableQuests);
    You can manipulate it without affecting the original.

    Now in regards to this:
    Code (CSharp):
    1. Quest newQuest1 = availMissions[Random.Range(0, availMissions.Count - 1)];
    Random.Range excludes the max, so you want to pass the number of elements (not count -1).
     
    Kiwasi likes this.
  6. Cyrussphere

    Cyrussphere

    Joined:
    Jul 1, 2016
    Posts:
    129
    Thanks for this! A Deep Copy is what I needed but only just learned the term (explains why my googling was failing). Seems a more simplified way to do the random choice as well. Ill give this a go when I get home from work.
     
  7. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    He doesn't do a deep copy. It's a shallow copy, which can be written in one line using AddRange(...) or using the constructor.

    Take a look at the shuffling approach, it avoids calling RemoveAt(INDEX) which internally has to shift all elements (except when it's the last element you remove, usually not a big deal unless you do it very often).
     
    Last edited: Feb 23, 2017