[C++] 4D Multimap

Hello there, I'm Rii ^o^

I'm trying to read a .csv file into a 4D multimap in C++(using Visual Studio 2010), but I'm stuck at entering data into that multimap, I've tried googling for the errors I'm getting but they all say it's a warning and it should be ignored (Warning C4503), though it creates an error for me! >_<"

The table of the file is like: http://puu.sh/vVHZ (don't worry it's a safe image link) This is all that matters in the table: http://puu.sh/vVNk
The data under "Compound" can become more or less, hence why I need it to be a multimap, and the treatments at the top "DMSO" etc. can also become less or more.

this is the structure for the multimap I wanna try making: http://puu.sh/vVN0 (le terribad paint skills)

I've tried two different methods that I understood already:

Method 1:
1
2
3
4
5
6
7
8
9
10
11
12
#include <map>
#include <string>
#include <vector>
#include <iostream>

using namespace std;

int main()
{
	multimap <int, multimap<string, multimap<string, multimap <string, double>>>> pathway;
	pathway.insert(pair<int, pair<string, pair<string, pair<string, double>>>> (1, ("cp", ("tr", ("dlc", 4.23)))));
}

Errors Method 1:
1>------ Build started: Project: map template, Configuration: Debug Win32 ------
1>Build started 21-5-2012 11:14:16.
1>InitializeBuildStatus:
1> Creating "Debug\map template.unsuccessfulbuild" because "AlwaysCreate" was specified.
1>ClCompile:
1> main.cpp
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\map(304): warning C4503: 'std::allocator<_Ty>::rebind<_Other>' : decorated name length exceeded, name was truncated
1> with
1> [
1> _Ty=std::pair<const int,std::multimap<std::string,std::multimap<std::string,std::multimap<std::string,double>>>>
1> ]
1> and
1> [
1> _Other=std::_Tree_nod<std::_Tmap_traits<int,std::multimap<std::string,std::multimap<std::string,std::multimap<std::string,double>>>,std::less<int>,std::allocator<std::pair<const int,std::multimap<std::string,std::multimap<std::string,std::multimap<std::string,double>>>>>,true>>::_Node
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\map(302) : while compiling class template member function 'std::multimap<_Kty,_Ty>::multimap(void)'
1> with
1> [
1> _Kty=int,
1> _Ty=std::multimap<std::string,std::multimap<std::string,std::multimap<std::string,double>>>
1> ]
1> c:\users\rianne\documents\visual studio 2010\map template\main.cpp(32) : see reference to class template instantiation 'std::multimap<_Kty,_Ty>' being compiled
1> with
1> [
1> _Kty=int,
1> _Ty=std::multimap<std::string,std::multimap<std::string,std::multimap<std::string,double>>>
1> ]
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(163): error C2664: 'std::pair<_Ty1,_Ty2>::pair(const std::pair<_Ty1,_Ty2> &)' : cannot convert parameter 1 from 'double' to 'const std::pair<_Ty1,_Ty2> &'
1> with
1> [
1> _Ty1=std::string,
1> _Ty2=std::pair<std::string,std::pair<std::string,double>>
1> ]
1> Reason: cannot convert from 'double' to 'const std::pair<_Ty1,_Ty2>'
1> with
1> [
1> _Ty1=std::string,
1> _Ty2=std::pair<std::string,std::pair<std::string,double>>
1> ]
1> No constructor could take the source type, or constructor overload resolution was ambiguous
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(247) : see reference to function template instantiation 'std::_Pair_base<_Ty1,_Ty2>::_Pair_base<_Ty,double>(_Other1 &&,_Other2 &&)' being compiled
1> with
1> [
1> _Ty1=int,
1> _Ty2=std::pair<std::string,std::pair<std::string,std::pair<std::string,double>>>,
1> _Ty=int,
1> _Other1=int,
1> _Other2=double
1> ]
1> c:\users\rianne\documents\visual studio 2010\map template\main.cpp(33) : see reference to function template instantiation 'std::pair<_Ty1,_Ty2>::pair<int,double>(_Other1 &&,_Other2 &&)' being compiled
1> with
1> [
1> _Ty1=int,
1> _Ty2=std::pair<std::string,std::pair<std::string,std::pair<std::string,double>>>,
1> _Other1=int,
1> _Other2=double
1> ]
1>
1>Build FAILED.
1>
1>Time Elapsed 00:00:01.12
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========


Method 2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <map>
#include <string>
#include <vector>
#include <iostream>

using namespace std;

struct tr
{
	string path;
	double value;
};

multimap <int, multimap<string, multimap<string, vector<tr>>>> pathwayMap;

void addData(int _index, string _cp, string _treatment, string _path, double _value)
{
	tr TR;
	TR.path = _path;
	TR.value = _value;
	pathwayMap.insert(_index, (_cp, (_treatment, (.push_back(TR))))); //it already says it goes wrong here
}

//empty main
int main()
{
	cout << endl;
	system("PAUSE");
	return(0);
}

Errors Method 2:
1>------ Build started: Project: map template, Configuration: Debug Win32 ------
1>Build started 21-5-2012 10:25:27.
1>InitializeBuildStatus:
1> Touching "Debug\map template.unsuccessfulbuild".
1>ClCompile:
1> main.cpp
1>c:\users\rianne\documents\visual studio 2010\map template\main.cpp(29): error C2664: 'std::_Tree_iterator<_Mytree> std::multimap<_Kty,_Ty>::insert<Data&>(std::_Tree_const_iterator<_Mytree>,_Valty)' : cannot convert parameter 1 from 'int' to 'std::_Tree_const_iterator<_Mytree>'
1> with
1> [
1> _Mytree=std::_Tree_val<std::_Tmap_traits<int,std::multimap<std::string,std::multimap<std::string,std::vector<Data>>>,std::less<int>,std::allocator<std::pair<const int,std::multimap<std::string,std::multimap<std::string,std::vector<Data>>>>>,true>>,
1> _Kty=int,
1> _Ty=std::multimap<std::string,std::multimap<std::string,std::vector<Data>>>,
1> _Valty=Data &
1> ]
1> and
1> [
1> _Mytree=std::_Tree_val<std::_Tmap_traits<int,std::multimap<std::string,std::multimap<std::string,std::vector<Data>>>,std::less<int>,std::allocator<std::pair<const int,std::multimap<std::string,std::multimap<std::string,std::vector<Data>>>>>,true>>
1> ]
1> No constructor could take the source type, or constructor overload resolution was ambiguous
1>
1>Build FAILED.
1>
1>Time Elapsed 00:00:00.54
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========


I am trying to master multimaps from scratch, but I can't find how to make this >_<"

All help is highly appreciated!

Thanks in advance!

~ Rii
Last edited on
I'm quite sure you don't need a 4D multimap. Quite honestly, I don't even think you need a multimap.

Can you perhaps take the time to explain what you want to do with the data?
@Gaminic: Visualizing a steriod pathway, in a graphical interface with the windows forms application. (should look a bit like http://puu.sh/vW31 with different colors to see certain increases and decreases, but that's a lot of calculating)

The information on the first column under "Compound" will be used to display names in the program. (this can become more or less, files can vary)

The information in the first row "DMSO" "Trilostane" etc.(ignore empty spaces) will be used to identify the treatment on the compound, and will be used in a calculation compared to other treatments for each seperate compound. (amount of treatments can increase depending on file)

In short:

- Read file
- Get names dynamically from first column
- Get treatments dynamically from first row while ignoring empty space
- Give the treatments a value. (and also the value of a path which is not really that important but nice to have)
- Use the values later in calculation (which is why they need to be a double)

Pathway -> Name (Compound) -> Treatment -> Value (& optionally that file path on the second row)


I use windows forms for the main application, but right now I'm trying to get a bit of it working in C++ console mode so I can implant it later in the windows forms application.

~ Rii


Edit: what I'd like to know is how to make a proper place to store that data in to load it later on whenever I like, NOT how to do create the whole program with calculations etc, just the data container + filling it
Last edited on
Multimaps and similar data structures are a way of organizing data, not visualizing. The choice of which data structure you wish you use depends on the operations you'll be doing with the data, not by the structure/meaning of the data.

In terms of your "list of steps": The first three are just reading in data. This has nothing to do with the underlying data structure. I'm guessing the same is true for the value (where does it come from?). The last part depends on what the calculations are, but again, I'm guessing you don't need a multimap (and definitely not a 4D one).

I'm having a bit of difficulty seeing the link between your pictures. The first one suggest a simple database, where I'd say a simple array with pre-sorted indeces is sufficient for anything. The second one lends itself to a 2D matrix, to look up the value of i->j.

The third picture (the tree-like structure) shows something completely different. In pic 2, every column has one location (string), while in pic 3, every pair (row i, column j) has a location. Are you sure that's correct? Wouldn't the location for DMSO be the same regardless of the compound on the first "level" of the tree?

The last picture shows some sort of structure through the compounds, thus only the row-values. Since the columns from the first two pictures are ignored, are you sure this part belongs to the same problem?
Thanks for your reply! and sorry I'm bad at explaining n.n

The first picture is just the whole table, not too important, just for an image of the whole file incase needed. http://puu.sh/vVHZ
The second picture is the table with irrelevant parts taken out. http://puu.sh/vVNk
The third picture is the structure I want to make from the table, but not sure how to make it properly. http://puu.sh/vVN0
The last picture is the product I want to make at the very end.


But yes I want to organize the data for now in a data structure.

The value comes from the number that's located under the DMSO, but only 1 value.
Each Compound has a certain amount of Treatments(DMSO, Trilostane, Metyrapone, etc) and each of those Treatments have a value.
Example: Compound -> Aldosterone -> DMSO -> 0
Compound -> Cortisol -> DMSO -> 8073
Compound -> Cortisol -> Trilostane -> 438

The location for DMSO(and also the path "D:\Lc D...") would be the same I guess, but I have no idea how to handle that, and I just know that each compound has a dmso with a value.

In the fourth picture is the visualisation, the colors should be green or red or grey depending on the calculations made with the values the treatments (DMSO) have. It's part of the same problem. (I have to make the fourth picture out of the first picture actually)

meh, sorry for this, I'm new to data structures and was told multimaps are good for this, the 2nd/3rd picture I made were my ideas of handling the data properly, but I honestly have no idea if it's good at all, and I'm really starting to doubt if it is.

Some extra info: The names of the compound can change/increase/decrease for each .csv file I get (Aldosterone could be DHEA in another file), the same counts for the treatments, (DMSO could be Abiraterone in another file)

Edit: sorry for editing late, but now that I look at the table it could be 2d maybe, since the x would be for example Cortisol and the y would be DMSO and then the value of that would be 8073, but then that brings me to the problem of that I also need to read the Compounds whose names can change >_< meh sorry I really have no idea how to handle that, I thought maybe if I added this extra piece of information it might help a bit
Last edited on
Ignoring the design questions, to compile your first example, understand that the members of your multimap are multimaps. To insert them, you need to create them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <map>
#include <string>
#include <vector>
using namespace std;
int main()
{
    multimap<int,
        multimap<string,
            multimap<string,
                multimap<string, double>>>> pathway;

    multimap<string, double> m3;
    multimap<string, multimap<string, double>> m2;
    multimap<string, multimap<string, multimap<string, double>>> m1;
    m3.insert(make_pair("dlc", 4.23));
    m2.insert(make_pair("tr", m3));
    m1.insert(make_pair("cp", m2));
    pathway.insert(make_pair(1, m1));
}

online demo: http://ideone.com/rDkOJ



@Cubbi: Thank you for your reply!
It compiles perfectly, sorry to ask another question, but how do you print the results on the screen with cout? The current loop I have doesn't work, and does this mean I need to make multiple loops/iterator? like one for m1, another one for m2.. etc.

1
2
3
for (multimap<int, multimap<string, multimap<string, multimap <string, double>>>>::const_iterator iter =pathway.begin();
	iter != pathway.end(); ++iter )
	cout << iter->first << '\t' << iter->second << '\n';

Error: error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const std::multimap<_Kty,_Ty>' (or there is no acceptable conversion)

edit: it's starting to get faulty at the "<< iter->second" part

Thanks again! and sorry!
Last edited on
Yeah, operator<< has to be overloaded for any non-standard object you wish to print, because the compiler doesn't know how to print it.

In this case, I'm not even sure what the object is. The iterator walks over the outer map, so I'm guessing the iterator contains a multimap<string, multimap<string, multimap<string, double>>>, but I'm not entirely sure that's useful.

Even if you somehow turn this into working code, you still won't have a working concept.

Just noticed you had a new post:

I tend to agree with your edit: it's most likely a 2D structure you're looking for. You want every pair (Compound, Treatment) to have a value. Regardless of the exact amount of Compounds and Treatments, you can already say that there will be C * T values.

Using this info, you can save the values in a simple, dynamically sized matrix. There are many ways to implement this, but I'll just give an example of what I think is the easiest way to explain and understand.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>
#include <iostream>
typedef std::vector<double> row;
typedef std::vector<row> matrix;

// Note: vectors are just arrays that can be (re)sized during runtime.
// Once initialized, using them is the same as you would an array.

matrix myValues; // init an empty matrix
// Assume we read in the amount of compounds (C) and treatments (T) here.
myValues.resize(C); // The matrix now has C rows: one for each compound.
for (int i = 0; i < C; ++i) myValues[i].resize(T); // Now every row has T columns.

// Accessing values:
std::cout << "The value of compound 4, treatment 9 is: "  << myValues[3][8] << std::endl;

This is definitely not the best way, but I think it's easy to see the logic behind it as every step is done explicitly, and it gives a nice look at nesting data types.

Each element of myValues (i.e. the outer vector) is an entire row. myValues[5] corresponds to the 6th compound row (if you start counting at 1). It doesn't have much use by itself, expect providing access to the inner vectors, which hold the values. myValues[5][8] looks up the 6th element of the outer vector, which returns an inner vector, and then looks up the 9th element of the inner vector, which is a double.


If you also want to keep a list of "which row/column is which compound/treatment": I'd keep that separate. Just keep an array/vector of strings for the rows, and one for the columns. Need to know the meaning behind myValues[5][8]? Look up myCompounds[5] and myTreatments[8].

I'm still not sure what steps are taken to get to picture 4.
Gaminic thank you so much! this is perfect!

I needed a good structure with easy access, now I can finally continue on the project, it was confusing because everyone said something different at work, and I didn't understand anything about creating structures. @.@

Thank you for explaining this in an easily understandable way, building this structure will be easy now!
Making something that reads the amount of rows/columns won't be a problem, so I won't be bothering anyone with that.

I'll keep the compound/treatment seperate like you suggested.

About picture 4: It's not important for now, it requires certain calculations, but I know which ones are needed, so no worries about that!


Thanks again! sorry if it sounds weird with all the "thank you"s but I'm just happy xP

~ Rii
Last edited on
You're welcome! If you have more questions, don't hesitate to ask.
I tink it would be even better to keep the original idea of using a some kind of map, but instead of using 3 sub multimaps, use a simple map wit a key type that is a structure containing the 3 'index' attributes (like in a database you would make a primary key of 3 attributes), and a specific comparison operator for the structure, needed for the map

This way would use a lot less memory than bulding N vectors of vectors, since only the actual data would use memory, not every position for every possible (and maybe physically impossible) combination of the 3 attributes would need to be available in memory. Unless of course all combinations of these 3 attributes have an actual value

But as gaminic said in his second post, it also depends on how you want to use the data!
Late edit, I just saw bartoli's post, sorry for this >_<

@bartoli: I like that idea, I might try it later, it's good programming practise, for this project my supervisors said that memory is no issue for this project(small program), but when optimizing it later on it's a good thing to do. And if I run out of time, I'll try it for a personal project myself, I like learning \:D/
The way the data will be used can be managed with the 2D matrix.

@Gaminic: I will! And I'm not sure if I have to ask it in a new topic or this one O.o but I'll ask it in this one for now.

hehe well I said the row/column reader wouldn't be a problem =///= but I kinda have a small problem with it in my eyes, wrote a very simple reader, it's a working thing, but I'm not sure if it's stable.

I'll minimize the amount of code, letting out all un-related stuff.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <fstream>

using namespace std;

int setXY(string filename, int *rows, int *columns)
{
	// ===== TODO LIST =====
	// (y)Read all "/n" in rows variable.
	// (?)Read all "," seperators for 1 row into columns variable.
	// () = TODO, (y) = good enough, (n) = not working, (?) = bad.

	char c;
	int row = 0;
	int cell = 0;

	ifstream is;
	is.open(filename);
	while (is.good())
	{
		c = is.get();		
		
		if (c == '\n') 
		{
			row++;
		}
		if (c == ',') 
		{
			cell++;
		}
	}
	is.close();
	cout << "Rows = " << row << endl << "Columns = " << cell / row << endl << "Cells = " << cell << endl << endl;
	
	*rows = row;
	*columns = cell / row; //this is what bothers me, I'm not sure if it's stable
	return(*rows, *columns);
}

int main()
{
	int C, T;
	setXY("example.csv", &C, &T);
	cout << "C = " << C << ", T = " << T << endl;

	cout << endl << "Press ENTER to continue...";
	cin.ignore(numeric_limits<streamsize>::max(), '\n' );
	return 0;
}

Rows = 21
Columns = 13
Cells = 273

C = 21, T = 13


The output values are 100% correct, but I am kinda bothered by the way the columns are calculated, if I have to devide the cells by the amount of rows all the time, will it still be stable?
I tried something like:
1
2
3
4
5
6
7
while (row == 0)
		{
			if (c == ',') 
			{
				cell++;
			}
		}
But that just makes the program do nothing anymore :l

Sorry if I had to put this in a new topic, since it's related to this current program I don't know.
Last edited on
Should be correct*. Given that there are N = T*C items (cells), it's easy to see that C = N/T = C*T/T = C.

I'm guessing your attempted fix of the row count is an infinite loop. Is "row" ever changed in the body of the while loop?

Also mind that return(*rows, *columns); doesn't do what you expect. The comma operator is generally not what you need. Use it in function definitions and declaring several variables of the same type, but nowhere else.

I'm guessing that's just left-over code though, since you've correctly changed it to change-through-parameters.

Additional tip: you're passing "rows" and "columns" as a pointer so you can change them inside the function. That's completely correct and valid, but it's often easier to pass them as a reference:

1
2
3
4
5
6
7
8
9
10
11
void changeMyInt(int* A) {
   *A = 5; // Need to dereference to change the value.
} 
void changeMyInt2(int& A) {
   A = 5; // A "is" the variable passed as a parameter.
}
int main() {
    int myInt = 3;
    changeMyInt(&myInt); // Need to get the address.
    changeMyInt(myInt); // Just pass the variable.
}


It's mostly a matter of preference in this case, but most people feel it enhances the readability. By pointer is completely correct too!



*Note on columns calculation:
Do mind that this isn't true for non-full matrices. Now, you know that N will be divisible by T and C (because it's T*C), and the result will be exact. However, should the input be the amount of items (N) and you're dividing it over a chosen amount of rows R, then you can be wrong, if N turns out to be not perfectly divisible by R.

E.g.: Dividing 50 items over 6 rows -> 50/6 = 8.3333, which becomes 8 due to integer division/truncation. Rounding upwards gives you the correct number.

In general, you can say that if you have N items and C items per row, then item I will be found at myMatrix[i/C][i%C]. If that doesn't make sense to you, ignore it for now.
I'm glad it's correct then! Would it be smart to make the integer round upwards as a failsafe? (though I highly doutb I'll be dealing with such tables)

For the row integer:
1
2
3
4
if (c == '\n') 
{
	row++;
}
This is the only place where it ever gets changed.

Oops, yes the return thing was a mistake, the function should be a void, stupid of me >_<"

I will try passing it by reference like that, cleaner code, better code! I followed bucky's tutorial for my current method hehe.
vid: http://thenewboston.org/watch.php?cat=16&number=39

thank you so much for your help again!
There's no point in rounding for this problem, as you said.

I'm not entirely sure what you're doing with while (row == 0), but I suggest dropping it, as the cell/rows method is exact.
Topic archived. No new replies allowed.