1

I have the following file input.txt which contains the values :

0 0
0 1
0 2
0 3
1 1
1 4
2 1
2 4
2 3

I want to insert these values to two arrays. I want them to look like this in the end.

0   0 1 2 3
1   1 4
2   1 4 3

First array will contain the id and second array will contain the elements. I have managed to dynamically allocate memory for these two arrays using the following :

int n,m,i=0,tempNum,lines;
int NumberOfIds=0;
ifstream read("input2.txt");
while(read>>n>>m){
    if (i==0){
        tempNum=n;
    }
    if (tempNum != n){
        NumberOfIds++;
    }
    tempNum = n;
    i++;
}
lines=i-1;

//printf("%d", j);
int** idElements = new int*[NumberOfIds];
int* ids = new int[NumberOfIds];
int* numberOfIdElements = new int[NumberOfIds];

// Rewinds file
read.clear();
read.seekg(0, ios::beg);

int counter = 0;
NumberOfIds=0;
while(read>>n>>m){

    if (tempNum != n){
        numberOfIdElements[NumberOfIds] = counter;
        NumberOfIds++;
        counter = 0;
    }
    tempNum = n;
    counter++;
    i++;
}

for(int k = 0; k < NumberOfIds; ++k)
    idElements[k] = new int[numberOfIdElements[k]];

Now I am stuck in how to insert the data. Any help would be appreciated.

thelaw
  • 570
  • 4
  • 14

2 Answers2

2

You can do this with std::vector's fairly easily:

std::vector<std::vector<int>> arr;
while(read>>n>>m){
    //if we're too small, push empty rows till we're big enough
    while(n + 1 > arr.size()) {
        arr.push_back(std::vector<int>());
    }
    arr.at(n).push_back(m); //push m onto row n
}

Alternatively, if your first column numbers become large (so you wouldn't want to make many empty rows), you can use an std::map:

std::map<int, std::vector<int>> arr;
while(read>>n>>m){
    //works because map subscript creates a key/val pair if one doesn't exist
    //so if this row doesn't exist, we get a new vector to push_back to
    arr[n].push_back(m); //push m onto row n
}

See it in action here: ideone

scohe001
  • 15,110
  • 2
  • 31
  • 51
  • Thank you for your answer. I am new in c++. Can you please guide me on how to print the contents of a vector? – thelaw May 07 '18 at 18:14
  • You can iterate over an `std::vector` the same way you would an array: `for(int x = 0; x < arr.size(); x++) { cout << arr[x] ... }` or since it's an STL container you can use fancier methods with iterators `for(auto x : arr) { ...`. For more, see [here](https://stackoverflow.com/questions/10750057/how-to-print-out-the-contents-of-a-vector). – scohe001 May 07 '18 at 18:15
  • @scohe001 afaik neither iterator based loops nor the new range based ones need stl containers, they both should work also with c arrays – 463035818_is_not_an_ai May 07 '18 at 18:18
  • @user463035818 I guess I just haven't had a need to use c-style arrays since I've started using range based loops...thanks for the catch! – scohe001 May 07 '18 at 18:20
  • i used your vector of vectors method and I print them like this : for(int x = 0; x < arr.size(); x++) { cout << arr[x] << endl; } How do I print the elements of the first column? – thelaw May 07 '18 at 18:50
  • @thelaw I made you a quick example of 3 options here: [ideone](https://ideone.com/mymg18). Since we have a vector of vector's, in your comment, `arr[x]` is a vector, so you'd need two loops to print like that (see `OPTION 2` in my link) – scohe001 May 07 '18 at 20:44
  • @scohe001 thank you very much! One last thing I don't understand is what you mean by this "Alternatively, if your first column numbers become large (so you wouldn't want to make many empty rows)". In my actual input.txt I have over 100k rows. Is that gonna be a problem? – thelaw May 07 '18 at 20:59
  • 1
    @thelaw the way the vector implementation is doing it is by looking at how many rows we've currently made and then looking at which row the next number will belong in. If the next row is bigger than how many we have allocated, it'll keep allocating until we have that many rows. This could be a problem if you only have values for rows `1, 100, 10000`. In that case, you'd end up creating `10000` vectors and `9998` would be useless, whereas the map implementation would only make 3. – scohe001 May 07 '18 at 21:01
  • @scohe001 Oh thanks you made everything crystal clear. The truth is I might end up with some useless vectors since the numbers aren't in order from 1 to 100000. I will consider using the map method. – thelaw May 07 '18 at 21:06
  • @thelaw order doesn't matter (ie, if you have data to insert to rows `3, 1, 5, 2, 4` in that order). The issue is if after you've processed ALL the data you're left with a bunch of empty rows. However, the `map` also has its drawbacks...it'll be harder to print your data and iterate through it since the `map` is using a tree under the hood. – scohe001 May 07 '18 at 21:09
1

Here is another approach using a map of sets:

#include <sstream>
#include <string>
#include <iostream>
#include <fstream>
#include <map>
#include <set>

int main()
{
    std::map<int, std::set<int>> m;
    std::ifstream ifs{"input.txt"};

    // read 
    for(std::string line; std::getline(ifs, line);){
        std::stringstream ss{line};
        int key;
        ss >> key;
        for (int val; ss >> val;)
            m[key].insert(val);
    }

    // print
    for(auto i : m) {
        std::cout << i.first << '\t';
        for(auto j : i.second)
            std::cout << j << ' ';
        std::cout << '\n';
    }
}

You might select other containers depending of your requirements for sorting or preserving duplicates.

wally
  • 10,717
  • 5
  • 39
  • 72