У меня есть:
map<string, map<int,int>>
Есть ли способ напечатать содержимое этой карты в алфавитном порядке, но без учета регистра? Например, распечатайте его в следующем порядке:
A : 1:1, 2:2
a : 3:1
an : 2:1
And : 4:1
and : 3:1
В настоящее время я печатаю с использованием следующего:
for (auto it = tokens.begin(); it != tokens.end(); ++it){
cout << it->first << " : ";
auto const &internal_map = it->second;
for (auto it2 = internal_map.begin(); it2 != internal_map.end(); ++it2){
if (it2 != internal_map.begin())
cout << " , ";
cout << it2->first << ":" << it2->second;
}
cout << endl;
}
Это печатает все, однако сначала идет все заглавные буквы, а затем все строчные. Например:
A : 1:1, 2:2
And : 4:1
a : 3:1
an : 2:1
and : 3:1
Есть ли способ напечатать содержимое этой карты в алфавитном порядке, но без учета регистра?
Да.
Вам нужно будет создать пользовательский функтор сравнения, который сравнивает две строки без учета регистра.
struct cicompare
{
bool operator()(std::string const& lhsIn, std::string const& rhsIn) const
{
char const* lhs = lhsIn.c_str();
char const* rhs = rhsIn.c_str();
for ( ; *lhs != '\0' && *rhs != '\0'; ++lhs, ++rhs )
{
if ( tolower(*lhs) != tolower(*rhs) )
{
return ( tolower(*lhs) < tolower(*rhs) );
}
else if ( *lhs != *rhs)
{
if ( *(lhs+1) == '\0' && *(rhs+1) == '\0' )
{
return (*lhs < *rhs);
}
}
}
return (tolower(*lhs) < tolower(*rhs));
}
};
Используйте функтор сравнения без учета регистра для создания карты.
map<string, map<int,int>, cicompare> mymap;
Если вы не хотите хранить упорядоченную карту без учета регистра, создайте копию исходной карты, используя cicompare
как раз перед печатью и распечатайте новую карту.
map<string, map<int,int>, cicompare> mapForPrinting;
mapForPrinting.insert(originalMap.start(), originalMap.end());
Как указано в принятом ответе, вы хотите использовать map
с пользовательской функцией сравнения. Хитрость заключается в правильном сравнении. Вы не хотите полностью нечувствительного к регистру сравнения, иначе «И» и «и» будут равны, и на карте будет только один из них. Ваши образцы данных не покрывают все случаи; например, какой будет порядок «An», «И», «AN», «И»? Следующая функция сравнения упорядочивает их «AN», «An», «AND», «And» — более короткие строки всегда меньше длинных строк с одинаковыми символами, и первый символ, который имеет другой регистр, — это прерыватель связей с верхним регистр перед строчными
struct CaseAwareCompare
{
bool operator()(const char * left, const char * right) const
{
bool tied = true, tiebreaker = false;
int i;
for (i = 0; left[i] != 0; ++i)
{
if (right[i] == 0)
return false;
if (tolower(left[i]) != tolower(right[i]))
return tolower(left[i]) < tolower(right[i]);
if (tied && left[i] != right[i])
{
tied = false;
tiebreaker = left[i] < right[i];
}
}
return (right[i] != 0) || (!tied && tiebreaker);
}
bool operator()(const string & left, const string & right) const
{
return operator()(left.c_str(), right.c_str());
}
};
Я боролся с тем, что назвать это; это не чувствительное к регистру сравнение, потому что оно различает входные данные с различным регистром. Я наконец решил, что это следует называть сравнением с учетом конкретного случая.
Вы можете хранить данные в векторе. Вы можете хранить данные как structure
или же pair
в векторе. Если вы используете pair
затем,
vector< pair< string, map<int,int> > > needToSort;
Тогда вы можете позвонить std::sort
в этом.
sort(needToSort.begin(), needToSort.end(), compareFunction);
compareFunction
является,
bool compareFunction( pair< string, map<int,int> > firstElement, pair< string, map<int,int> > secondElement)
{
string firstString = firstElement.first;
string secondString = secondElement.first;
for(int i=0;i<firstString.size();i++)
firstString[i] = toLower(firstString [i]);
for(int i=0;i<secondString.size();i++)
secondString [i] = toLower(secondString[i]);
return firstString < secondString;
}
а затем вы можете распечатать вектор, который должен дать вам желаемый результат.
Я думаю, что обычный способ сделать это — создать индекс итераторов для отображаемых элементов:
// Return a range of iterators to the elements
// of the given range.
template <typename Range>
auto make_index(Range& range)
-> vector<decltype(range.begin())> {
vector<decltype(range.begin())> index;
for (auto i = begin(range), e = end(range); i != e; ++i) {
index.push_back(i);
}
return index;
}
и отсортировать итераторы в соответствии с критериями отображения:
auto index = make_index(tokens);
using iterator = decltype(tokens.begin());
sort(begin(index), end(index), [](iterator a, iterator b){
return boost::algorithm::ilexicographical_compare(a->first, b->first);
});
помня разыменовать итераторы для отображения:
cout << "Sorted:\n";
for (auto&& i : index) {
cout << i->first << ':';
for (auto const& j : i->second) {
cout << ' ' << j.first << ':' << j.second;
}
cout << '\n';
}
(Смотрите все это живут в Колиру).
Я использовал boost::algorithm::ilexicographical_compare
, Повышение нечувствительного к регистру сравнения строк, зависящего от локали, чтобы сохранить некоторую типизацию.