Pick a random element from IList <> other than this

I have this template where I need to select any random item from a list other than the current one (passed as an argument). I came up with this method:

public Element GetAnyoneElseFromTheList(Element el)
{
  Element returnElement = el;
  Random rndElement = new Random();
  if (this.ElementList.Count>2) 
  {
  while (returnElement == el) 
  {
    returnElement = this.ElementList[rndElement.Next(0,this.ElementList.Count)];  
  }

  return returnElement;
  }
 else return null;
}

      

But while the cycle bothered me all day and night, I need to sleep. Any other good approaches to this? i.e. what is returned with a fixed number of steps?

Edit: In my case, the list is guaranteed to contain the "el" element to be avoided and the list does not contain duplicates, but it would be interesting to see even more general cases.

+2


source share


6 answers


public Element GetAnyoneElseFromTheList(Element el)
{
  Random rndElement = new Random();
  int index;
  if (this.ElementList.Count>1) 
  {
     index = rndElement.Next(0,this.ElementList.Count-1);
     if (this.ElementList[index] == el)
        return this.ElementList[this.ElementList.Count-1];
     else
        return this.ElementList[index];
  }
 else return null;
}

      



+7


source


public Element GetAnyoneElseFromTheList(Element el)
{
  if(this.ElementList.Count < 2) return null;
  Random rndElement = new Random();
  int random = rndElement.Next(0,this.ElementList.Count -1);
  if(random >= this.ElementList.indexOf(el)) random += 1;
  return this.ElementList[random];
}

      

Get a random number between 0 and the length of the list minus 2.

If this number is greater than or equal to the index of your item, add it to that number.

Returns the element at the index of this number



Edit: Someone mentioned in a comment, who then deleted, that this doesn't work that well if you have duplicates. In this case, the best solution would actually be.

public Element GetAnyoneElseFromTheList(int elId)
{
  if(elId >= this.ElementList.Count) throw new ArgumentException(...)
  if(this.ElementList.Count < 2) return null;
  Random rndElement = new Random();
  int random = rndElement.Next(0,this.ElementList.Count -1);
  if(random >= elId) random += 1;
  return this.ElementList[random];
}

      

Edit 2: Another alternative for duplicate items - you can use the optimized random sort (arbitrary sort) operation on the cloned version of the list and then execute it through the list. The foreach will iterate over the number of repeated items in the list. It all comes down to how optimized your shuffle algorithm is.

+8


source


I had to solve a similar problem. Here's what I will do:

public Element GetAnyoneElseFromTheList(Element el)
{   
     // take a copy of your element list, ignoring the currently selected element
     var temp = this.ElementList.Where(e => e != el).ToList();
     // return a randomly selected element
     return temp[new Random().Next(0, temp.Count)];
}

      

+2


source


First, calculate how many overlapping items to avoid. Then choose a random number based on the rest of the elements and find the selected element:

public Element GetAnyoneElseFromTheList(Element el) {
   int cnt = this.ElementList.Count(e => e != el);
   if (cnt < 1) return null;
   Random rand = new Random();
   int num = rand.Next(cnt);
   index = 0;
   while (num > 0) {
      if (this.ElementList[index] != el) num--;
      index++;
   }
   return this.ElementList[index];
}

      

+2


source


public Element GetAnyoneElseFromTheList(Element el)
{
    // first create a new list and populate it with all non-matching elements
    var temp = this.ElementList.Where(i => i != el).ToList();

    // if we have some matching elements then return one of them at random
    if (temp.Count > 0) return temp[new Random().Next(0, temp.Count)];

    // if there are no matching elements then take the appropriate action
    // throw an exception, return null, return default(Element) etc
    throw new Exception("No items found!");
}

      

If you are not using C # 3 and / or .NET 3.5, you need to do it a little differently:

public Element GetAnyoneElseFromTheList(Element el)
{
    // first create a new list and populate it with all non-matching elements
    List<Element> temp = new List<Element>();
    this.ElementList.ForEach(delegate(int i) { if (i != el) temp.Add(i); });

    // if we have some matching elements then return one of them at random
    if (temp.Count > 0) return temp[new Random().Next(0, temp.Count)];

    // if there are no matching elements then take the appropriate action
    // throw an exception, return null, return default(Element) etc
    throw new Exception("No items found!");
}

      

+2


source


What about:

 public Element GetAnyoneElseFromTheList(Element el)
  {
  Random rand = new Random();
  Element returnEle = ElementList[rand.Next(0, this.ElementList.Count];
  return returnEle == el ? GetAnyoneElseFromTheList(Element el) : el;
 }

      

Or in case you don't like the loop feature:

public Element GetAnyoneElseFromTheList(Element el)
 {
  Random rand = new Random();
  List<Element> listwithoutElement = ElementList.Where(e=>e != el).ToList();
  return listwithoutElement[rand.Next(listwithoutElement.Count)];
 }

      

+1


source







All Articles