c++stdvectorstdliststdset

Vector of set insert elements


I'm trying to write a function which will return vector of set type string which represent members of teams.

A group of names should be classified into teams for a game. Teams should be the same size, but this is not always possible unless n is exactly divisible by k. Therefore, they decided that the first mode (n, k) teams have n / k + 1 members, and the remaining teams have n / k members.

#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <list>
typedef std::vector<std::set<std::string>>vek;
vek Distribution(std::vector<std::string>names, int k) {
  int n = names.size();
  vek teams(k);
  int number_of_first = n % k;
  int number_of_members_first = n / k + 1;
  int number_of_members_remaining = n / k;
  int l = 0;
  int j = 0;
  for (int i = 1; i <= k; i++) {
    if (i <= number_of_first) {
      int number_of_members_in_team = 0;
      while (number_of_members_in_team < number_of_members_first) {
        teams[l].insert(names[j]);
        number_of_members_in_team++;
        j++;
      }
    }
    else {
      int number_of_members_in_team = 0;
      while (number_of_members_in_team < number_of_members_remaining) {
        teams[l].insert(names[j]);
        number_of_members_in_team++;
        j++;
      }
    }
    l++;
  }

  return teams;
}

int main ()
{
  for (auto i : Distribution({"Damir", "Ana", "Muhamed", "Marko", "Ivan",
                              "Mirsad", "Nikolina", "Alen", "Jasmina", "Merima"
                             }, 3)) {
    for (auto j : i)
      std::cout << j << " ";
    std::cout << std::endl;
  }
  return 0;
}

OUTPUT should be:

Damir Ana Muhamed Marko

Ivan Mirsad Nikolina

Alen Jasmina Merima

MY OUTPUT:

Ana Damir Marko Muhamed

Ivan Mirsad Nikolina

Alen Jasmina Merima

Could you explain me why names are not printed in the right order?


Solution

  • teams being a std::vector<...> supports random access via an index.
    auto & team_i = teams[i]; (0 <= i < teams.size()), will give you an element of the vector. team_i is a reference to type std::set<std::list<std::string>>.

    As a std::set<...> does not support random access via an index, you will need to access the elements via iterators (begin(), end() etc.), e.g.: auto set_it = team_i.begin();. *set_it will be of type std::list<std::string>.

    Since std::list<...> also does not support random access via an index, again you will need to access it via iterators, e.g.: auto list_it = set_it->begin();. *list_it will be of type std::string.

    This way it is possible to access every set in the vector, every list in each set, and every string in each list (after you have added them to the data structure).

    However - using iterators with std::set and std::list is not as convenient as using indexed random access with std::vector. std::vector has additional benefits (simple and efficient implementation, continous memory block).
    If you use std::vectors instead of std::set and std::list, vek will be defined as:

    typedef std::vector<std::vector<std::vector<std::string>>> vek;
    

    std::list being a linked list offers some benefits (like being able to add an element in O(1)). std::set guarentees that each value is present once. But if you don't really need these features, you could make you code simpler (and often more efficient) if you use only std::vectors as your containers.

    Note: if every set will ever contain only 1 list (of strings) you can consider to get rid of 1 level of the hirarchy, I.e. store the lists (or vectors as I suggested) directly as elements of the top-level vector.


    UPDATE:

    Since the question was changed, here's a short update:

    1. In my answer above, ignore all the mentions of the std::list. So when you iterate on the set::set the elements are already std::strings.
    2. The reason the names are not in the order you expect: std::set keeps the elements sorted, and when you iterate it you will get the elements by that sorting order. See the answer here: Is the std::set iteration order always ascending according to the C++ specification?. Your set contains std::strings and the default sort order for them is alphabetically.
      Using std::vector instead of std::set like I proposed above, will get you the result you wanted (std::vector is not sorted automatically).

    If you want to try using only std::vector:
    Change vek to:

    typedef std::vector<std::vector<std::string>>vek;
    

    And replace the usage of insert (to add an element to the set) with push_back to do the same for a vector.