///////////////////////////////////////////////// // struct FixedSizeRecord // Record FixedSizeRecord::GetRecord() const { Record r; r.AddField(Field(Field::STB, std::string(stb))); r.AddField(Field(Field::TITLE, std::string(title))); r.AddField(Field(Field::PROVIDER, std::string(provider))); r.AddField(Field(Field::DATE, std::string(date))); r.AddField(Field(Field::REV, dollars, cents)); r.AddField(Field(Field::VIEW_TIME, hours, mins)); return r; }
void GroupOperation::Run(std::vector<Record> &vRecords) { if (m_sGroupParam.empty()) throw std::runtime_error("Group parameter (-g) expects an argument"); //Only one group parameter supported by -g if (Utility::Tokenize(m_sGroupParam, ",:").size() > 1) throw std::runtime_error("Group parameter (-g) must be a single field"); //First collect the "-s" params std::vector<std::string> const vSelectTokens(Utility::Tokenize(m_sSelectFields, ",")); if (vSelectTokens.empty()) throw std::runtime_error("Group feature (-g) cannot be used without a SELECT list (-s)"); //The field we want to group by must be on the SELECT list, and must not be associated with an aggregate if (std::find(vSelectTokens.begin(), vSelectTokens.end(), m_sGroupParam) == vSelectTokens.end()) throw std::runtime_error("Group field (" + m_sGroupParam + ") must be on the SELECT list (-s) and cannot have aggregates"); //Now find aggregates associated with each SELECT field for (std::string const &s : vSelectTokens) { std::vector<std::string> const vNameValue(Utility::Tokenize(s, ":")); assert(!vNameValue.empty()); if (vNameValue.size() == 2) m_vFieldToAggregate.emplace_back(Field::ToEnum(vNameValue.front()), GetAggregate(vNameValue.back())); else if (vNameValue.size() == 1) m_vFieldToAggregate.emplace_back(Field::ToEnum(vNameValue.front()), Aggregate::NONE); else throw std::runtime_error("Invalid aggregate: \"" + s + "\""); } //Exactly one field must be in the SELECT list without aggregates if (std::count_if(m_vFieldToAggregate.begin(), m_vFieldToAggregate.end(), [](std::pair<Field::Name, Aggregate> const &p) { return (p.second == Aggregate::NONE); }) != 1) throw std::runtime_error("Exactly one field must be in the SELECT list (-s) without aggregates"); //Now let's do real work! //Step 1: Group records by the "group-by" field Field::Name const groupByField(Field::ToEnum(m_sGroupParam)); std::map<std::string, std::vector<Record>> mapGroupedRecords; for (Record const &rec : vRecords) mapGroupedRecords[rec.GetFieldValue(groupByField)].push_back(rec); std::vector<Record> vOutputRecords; //For each group of records... for (auto const &group : mapGroupedRecords) { std::vector<Record> const &groupRecords(group.second); Record newRecord; //For each aggregate... for (auto const &aggregate : m_vFieldToAggregate) { Field::Name const &name(aggregate.first); Aggregate const &aggr(aggregate.second); //Step 2: Aggregate field values across records in a group Field const newField(PerformAggregation(groupRecords, name, aggr)); newRecord.AddField(newField); } vOutputRecords.emplace_back(newRecord); } vRecords.swap(vOutputRecords); }