Help to make objects linked to others>?

Hi once again!
I’m really taking advantage of you guys :wink:

The question
So, i’m making a quiz app and have created a function that can be called and assign a FrameName to a sprite which will generate a random image.
I have put the names in a vector of std::strings and it works great but what i want is, now when it returns the images randomly i want to do something like this:

Let’s say i do this:

Abilities.push_back("ChampSquares/TheOneAb.png");
Then in the function that can be called from other classes:

string QuizGraphic::getRandObj(std::string object)
{
    if (object == "quiz") {
        if (!Abilities.empty())
        {
                       std::string name = Abilities[1]; (THIS IS NORMALLY A RANDOM NUMBER)
            return name; 
        }
    }
return NULL;
}

Then i got some variables:


//Variables
static int state;
   //Desription
static std::string description;

And i want to do so that if something like Abilities[random number] returns maybe “a specific name” make the description string to a text that describes that certain object.
Like if the returned object from the vector what an image of an apple i would make the description = “Apple”

Thanks!
Hope you get my point :slight_smile:
PS i’ve not checked the grammar…

Just use std::unordered_map or Map with the specific name as the key and the description as value.

The map also supports index access, so you can get a key/value pair by a randomized index.

You could also use a struct if you want to associate more than 2 pieces of information. For example,

struct Thing {
  std::string name;
  std::string description;
  std::string otherData;
};

You could also use a map like iQD said as well. For example,

struct Thing {
  std::string description;
  std::string otherData;
};

...

std::map<std::string, Thing> things;

...

things[name] = { "description", "otherData" };

That way you can get your data by name and get all your data associated with it.

1 Like

Do you prefer using structs instead of std::pair/std::tuple?

If so, could you please state the pros and cons in your opinion?

I haven’t used tuples before, so the main reason I didn’t mention them was because I didn’t know about them. But giving them a quick look it seems they are like ordered lists.

If this is the case, I’d prefer structs because you can refer to the variables inside by name. Tuples seem to require you to get the data by index, which could be annoying if you have a lot of data. (The internet does say you can use an enum sort of assign names to the tuple indices though.) I’m not sure what the benefits to tuples are. They might use slightly less memory, and seem like they have more functions to use with them.

Struct: std::string description = myStruct.description;
Tuple: std::string description = std::get<0>(myTuple);

I hope that’s right. As I said, I haven’t used tuples before before.

Ok thanks Guys!!!
But how can I add that to vector and then get only a random name?
I mean, I used to get a random object from the vector, the vector hold all the champions names as strings.

And how should that do so if the random object was, lets say,“apple” then the string description would be “des of the apple”?

I just don’t really get how to make it all stick together?

But I got the point with using Struct

You get me?

Thanks!

I see. I just had the impression you are more pro C :wink:

The API doc states the following:

Conceptually, they are similar to plain old data structures (C-like structs) but instead of having named data members, its elements are accessed by their order in the tuple.

Yes. They just define an enum to kind of get that named access. The get template just uses size_t for the index type.

Totally correct.

What I like about tuples, as you already mentioned, is their functions. Packing and unpacking is really convenient. Similar to scripting languages like python.

You don’t need a vector, as map/Map is your container. The cocos2d-x Map container also supports the randomized index.

Just do the same with Map.

// fill your map
Map<std::string, std::string> myMap;
myMap.insert("apple", "desc of the apple");

string QuizGraphic::getRandObj()
{
    return myMap.getRandomObject();
}

You should return an empty string instead of a null pointer. getRandomObject() checks if the container is empty, and returns a nullptr if so.

Because of this, it would be better to use a char * for the value of the map, as you cannot compare a std::string to a nullptr:

char* QuizGraphic::getRandObj()
{
    auto description = myMap.getRandomObject();

    return (description != nullptr) ? "" : description);       
}

But this has one problem. You are not allowed to use char * as a value type, as the value type can not be converted to Ref*.

So it’s better to stay away from Map and use std::unordered_map or std::map and use the cocos2dx or stdlib random functions.

Map just wraps std::map or std::unordered_map anyway and tries to add auto-release mechanisms to it.
In my opinion, the stdlib provides better constructs.

I have used std::pair and std::tuple a number of times to store pieces like screen width, heigh, scale factor. Pass it around where needed. Works well for me, for this.

What would be some use cases for something like having an std::tuple that contained std::tuples, lol.
Maybe a large list that had other lists associated with it’s list items?

Exactly. E.g. like a relational database/big data model. You have the tuple <name, cities, inhabitants>, with cities being another tuple with <zipcode, name> and inhabitants being another tuple with <name, address>.

With all the extra information being extra tuples, it’s easier to remove/add/switch information.

Of course inhabitants have to be added to the cities tuple to get the 3NF:
tuple country: <name, cities>
tuple cities: <zipcode, name, inhabitants>
tuple inhabitants: <name, address>

Ok thank you i got that.
But just to make it clear:
If i had those public variables:

static std::string currentDescription;
static std::string currentName;
static std::string currentAnswer;

And i want to make this stuff be linked together

// fill your map
Map<std::string, std::string, std::string> myMap;
myMap.insert("apple", "desc of the apple". "Answer");
I want to add more names, more descs and more answers, like if i added one more three associated items to the container, How? Could be something like 10 groups

Then i would return a random question: let’s say “apple” once again

char* QuizGraphic::getRandObj()
{
    auto question = myMap.getRandomObject(); here i want to get a random
name: lets say there are "apple", "pc" and "win" available..
    return (description != nullptr) ? "" : question);

Then i want to set
currentDescription;
currentName;
currentAnswer;      
To the three different associated items..
}

You understand what i mean?

Thanks

A map is a key/value pair.
Your value needs to be a tuple, but you can’t use Map for that, as the value is not convertible to Ref*. You have to use std::unordered_map or std::map.

std:unordered_map<std::string, std::tuple<std::string, std::string>> myMap;
int idx = cocos2d::RandomHelper::random_int(0, myMap.size() - 1);
std::tie(currentDescription, currentAnswer) = myMape.at(idx);

You can only access the key by iterating, but you can alternatively store the key additionally in the tuple:

std:unordered_map<std::string, std::tuple<std::string, std::string, std::string>> myMap;
int idx = cocos2d::RandomHelper::random_int(0, myMap.size() - 1);
std::tie(currentName, currentDescription, currentAnswer) = myMap.at(idx);

If you need the key itself, I would not use a map. Use a set, vector, list instead. The container depends on your needs like random access, iterating, accessing the container by the name as key and so on.

std::vector<std::tuple<std::string, std::string, std::string>> myVector;
int idx = cocos2d::RandomHelper::random_int(0, myVector.size() - 1);
std::tie(currentName, currentDescription, currentAnswer) = myVector.at(idx);

now i got exactly!
But could i also use this:
Do you think it would be smarter to do this

std::tuple<double, char, std::string> get_student(int id)
{
    if (id == 0) return std::make_tuple(3.8, 'A', "Lisa Simpson");
    if (id == 1) return std::make_tuple(2.9, 'C', "Milhouse Van Houten");
    if (id == 2) return std::make_tuple(1.7, 'D', "Ralph Wiggum");
    throw std::invalid_argument("id");
}

So I call it using that, so one tuple will actually be the container?
And could call it like this:

  auto student0 = get_student(0);
    std::cout << "ID: 0, "
              << "GPA: " << std::get<0>(student0) << ", "
              << "grade: " << std::get<1>(student0) << ", "
              << "name: " << std::get<2>(student0) << '\n';

Thanks x100000000000, seriously I really appreciate your time!

No, as it would waste more resources(e.g. CPU cycles), because of the comparison. It’s completely inflexible in many ways.

An index fetch into a map/vector to get the desired tuple is much faster and more efficient.
It’s also more convenient and flexible to insert new tuples.

If you want to add new tuples in your example, you would need to change your code by adding a new comparison.

Okay so am i right now…
So it would be better to create multiple tuples and add to a vector or to create a map with the name as the key value? If so, which one of those should i do
Sry but it is not 100% clear to me

Yes. Create the container and populate it.

  • If you don’t need the keys for further purposes and need the
    key/value pairs to be ordered, use a std::map.
  • If you don’t need the keys for further purposes and don’t need the
    key/value pairs to be ordered, use a std::unordered_map.
  • If you need the keys for further purposes and need the key/value
    pairs to be ordered, use a std::map and additionally insert the key into the value tuple.
  • If you need the keys for further purposes and don’t need the
    key/value pairs to be ordered, use a std::unordered_map and additionally insert the key into the value tuple.
  • If you need the keys for further purposes and don’t want to duplicate
    the keys in the value tuple, use a std::vector.
2 Likes

Omg! Look you are a great person! That you let me use so much of your time!!! Thannnkkkkssss!!!