Как сгруппировать (считать) на один столбец в C ++ - PullRequest
0 голосов
/ 09 июня 2019

У меня есть файл CSV, в котором есть 2 столбца, в каждой строке первый элемент - это идентификатор человека, а другой - его идентификатор друга. Я хочу сгруппировать идентификаторы людей по количеству их друзей в c ++, а затем отсортировать их по убыванию количества друзей; Как я могу это сделать?

мой CSV-файл выглядит так:

ID,Friend_ID
P0,P1
P0,P2
P0,P3
P1,P0
P1,P2
P1,P3
P2,P0
P3,P0
P3,P1

и я хочу это:

ID,Frind_count
P0,3
P1,3
P3,2
P2,1

1 Ответ

0 голосов
/ 09 июня 2019

Вам нужно объединить основную проблему в подзадачи. Исходя из ваших требований необходимы следующие шаги:

  1. Открыть входной файл
  2. Считать заголовок файла CSV-файла
  3. Считать полные данные из файла и проанализировать отдельные строки
  4. Подсчитайте, сколько друзей у каждого человека
  5. Сортировка результатов по количеству в порядке убывания
  6. Запись результата в выходной файл

Все это можно сделать в ~ 25 строках кода. Функция Main состоит из ~ 12 строк. Пожалуйста, смотрите ниже одно возможное решение. Это решение использует библиотеку std.

#include <map>
#include <string>
#include <iostream>
#include <fstream>
#include <utility>
#include <iterator>
#include <regex>
#include <vector>

// Make reading easier
using PairOfFriends = std::pair<const std::string, std::string>; // This is exactly one pair of friends. With a const member for multimap "type_value"
using MmFriend = std::multimap<std::string, std::string>; // And this are all pairs of frineds in a sorted multimap

struct PairLine     // ! This is a proxy for the input_iterator !
{   // Input function. Read on line of text file and split it into 2 parts
    friend std::istream& operator>>(std::istream& is, PairLine& line)
    {   // We will use a regex iterator to be a little more tolerant for different separator characters
        std::string wholeLine;  std::regex comma("[ \t\n]*[,;.][ \t\n]*");
        std::getline(is, wholeLine);    // Read one complete line and split it into 2 parts
        std::vector<std::string> part{ std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), comma, -1), std::sregex_token_iterator() };
        if (2 == part.size()) line.pairString = std::make_pair(part[0], part[1]); // Copy the 2 parts in our internal variable
        return is; 
    }
    operator PairOfFriends() const { return pairString; } // Cast pairString to PairOfFriends. For input iterator.
    std::pair<std::string, std::string> pairString{};  // Internal representation. Attention! No const member!
};
struct PersonCount
{   // We will use this POD to store person names and related counts
    std::string person; unsigned int personCount;  // And we need also an output function
    friend std::ostream& operator<< (std::ostream& os, const PersonCount& pc) { return (os << pc.person << "," << pc.personCount); }
};

int main()
{
    // 1. Open file. Will be closed by destructor ------------------------------
    std::ifstream inFileStream{ "r:\\person.csv" };             
    // 2. Read header ----------------------------------------------------------
    std::string header; std::getline(inFileStream, header);     
    // 3. Read the complete CSV file and put result into multimap --------------
    MmFriend mmFriend{ std::istream_iterator<PairLine>(inFileStream), std::istream_iterator<PairLine>() };
    // 4. Count how many friends persons have ----------------------------------
    std::vector<PersonCount> friendCount{}; // Person and Count will be stored here
    for (MmFriend::iterator it = mmFriend.begin(), end = mmFriend.end(); it != end; it = mmFriend.upper_bound(it->first)) { 
        friendCount.emplace_back(PersonCount{it->first, mmFriend.count(it->first) });  // Put count info into new arry
    }
    //5. Sort ------------------------------------------------------------------
    std::sort(friendCount.begin(), friendCount.end(), [](const PersonCount & p1, const PersonCount & p2) { return p2.personCount < p1.personCount; });
    // 6. Output ---------------------------------------------------------------
    std::ofstream outFileStream{"r:\\out.txt"};
    outFileStream << "ID,Friend_count\n";
    std::copy(friendCount.begin(), friendCount.end(), std::ostream_iterator<PersonCount>(outFileStream, "\n"));
    return 0;
}

Обратите внимание: для чтения CSV-данных и их анализа мы используем входной итератор. Весь разбор выполняется в основном в одну строку. Функции Extractor анализируют входную строку с помощью sregex_token_iterator. Если это должно быть расширено до более чем 2 записей, вместо стандартных пар можно использовать кортежи.

Остальное действительно легко. Я думаю, это не нуждается в дополнительном объяснении.

Тем не менее. При необходимости я, конечно, отвечу на вопросы.

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...