How to use IList for generating databases?

Viewed 62

I have a "database" generated from a random list of locations, which attaches a random continent and a random category to each one.

I wanted to change the RandomItem to NextItem so it just selects the next item of the list of locations rather than randomly selecting them, unfortunately I don't know enough to know what to search for to solve this problem.

I was hoping I could use the value of (i) when it generates the next place but that didn't seem to work.

How would I go about changing the NextItem to do this?

    public class TestPlacesRepository : PlacesRepository
    {

        const int placeCount = 10;
        readonly List<Location> locations;
        readonly List<Continent> continents;
        readonly List<Category> categories;
        readonly Random random;

        public TestPlacesRepository() : base()
        {
            //Generates the seed for the rng
            this.random = new Random((int)DateTime.Now.Ticks);
            this.locations = new List<Location>();
            this.continents = new List<Continent>();
            this.categories = new List<Category>();

            GenerateLocations();
            GenerateContinents();
            GenerateCategories();

            for (int i = 0; i < placeCount; i++)
                Places.Add(GeneratePlace(i));

        }

        Place GeneratePlace(int number)
        {
            //Creates the x "random" locations from the list of locations below
            Place place = new Place(
                NextItem<Location>(locations),
                RandomItem<Continent>(continents),
                RandomItem<Category>(categories),
                number % 3 == 0);
            return place;
        }

        T NextItem<T>(IList<T> list)
        {
            int index = (int)(i * (list.Count));
            return list[index];
        }

        T RandomItem<T>(IList<T> list)
        {
            int index = (int)(random.NextDouble() * 0.99 * (list.Count));
            return list[index];
        }

        void GenerateLocations()
        {
            //1st 10 places
            locations.Add(new Location("Temples of Angkor", "Cambodia"));
            locations.Add(new Location("Great Barrier Reef", "Australia"));
            locations.Add(new Location("Machu Picchu", "Peru"));
            locations.Add(new Location("Great Wall of China", "China"));
            locations.Add(new Location("Taj Mahal", "India"));
            locations.Add(new Location("Arizona - Grand Canyon National Park", "USA"));
            locations.Add(new Location("Colosseum", "Italy"));
            locations.Add(new Location("Iguazu Falls", "Brazil/Argentina"));
            locations.Add(new Location("Alhambra", "Spain"));
            locations.Add(new Location("Aya Sofya", "Turkey"));
         }
        void GenerateContinents()
        {
            //All 7 Continents
            continents.Add(new Continent("Africa"));
            continents.Add(new Continent("Antartica"));
            continents.Add(new Continent("Asia"));
            continents.Add(new Continent("Europe"));
            continents.Add(new Continent("North America"));
            continents.Add(new Continent("Oceania"));
            continents.Add(new Continent("South America"));
        }
        void GenerateCategories()
        {
            //All 7 Categories
            categories.Add(new Category("Activity/Modern Attraction"));
            categories.Add(new Category("District/Square/Town"));
            categories.Add(new Category("Historical Site"));
            categories.Add(new Category("Museum"));
            categories.Add(new Category("Nature/Landscape"));
            categories.Add(new Category("Palace/Castle/Fort/Ruin"));
            categories.Add(new Category("Religion"));
        }

type here

2 Answers

just use an index counter for each list

int nextLoc = 0;

Location NextLocation()
{
  var loc = locations[nextLoc];
  nextLoc++;
  return loc;
}

Another option that would also work with any IEnumerable (which includes IList) would be to get the enumerator and then simply call MoveNext and return Current for each iteration, e.g.

IEnumerator<T> enumerator = null;

T NextItem<T>(IEnumerable<T> collection)
{
    if (enumerator == null)
    {
        enumerator = collection.GetEnumerator();
    }

    if (!enumerator.MoveNext())
    {
        return null;
    }

    return enumerator.Current;
}

Note, you should also explicitly call enumerator.Dispose after you've finished using it (it implements IDisposible). E.g. drop something like the following at the end of your constructor:

if (enumerator != null)
{
    enumerator.Dispose();
}

On recent C# versions this can be simplified to:

enumerator?.Dispose();

There's an obvious caveat that it will only work for the first collection it is used with (as the collection parameter is only used to initialise the enumerator on first use).

Related