Ты был на правильном пути, когда решил перебирать разговоры. Попробуйте следующее:
conversations = Conversation.
where("receiver = :user_id OR sender = :user_id", user_id: currenct_user.id)
Message.where(conversation: conversations).
where.not(user: current_user).where(read: false).count
Я также предлагаю добавить область видимости unread
в вашу модель сообщений для чтения,
class Message < ApplicationRecord
...
scope :unread, -> { where(read: false) }
...
end
Теперь вы можете написать:
Message.where(conversation: conversations).
where.not(user: current_user).unread.count
Следующее имеет довольно некоторые шансы быть одним из самых быстрых в вопросах подсчета:
std::array<unsigned int, (1U << CHAR_BIT)> counts({ });
for(auto c : word)
counts[c]++;
Получение отдельных значений довольно эффективно:
std::cout << "a: " << counts['a'] << std::endl
Перебор букв - ну, потребуется небольшой трюк:
for(char const* c = "abcdefghijklmnopqrstuvwxyz"; *c; ++c)
// case-insensitive:
std::cout << *c << ": " << counts[*c] + counts[toupper(*c)] << std::endl;
Конечно, вы тратите немного памяти - это может стоить вам производительности снова: если массив больше не помещается в кеш ...
Общее и переносимое решение было бы
const std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
for (int i = 0; alphabet[i]; ++i)
amounts[i] = std::count(word.begin(), word.end(), alphabet[i]);
. Если можно предположить, что набор строчных букв является смежным диапазоном, это можно упростить до
for (char c = 'a'; c <= 'z'; ++c)
amounts[c - 'a'] = std::count(word.begin(), word.end(), c);
Нет (открытый ) if
выше. Конечно, ничто не мешает реализации std::count()
с его использованием.
Используйте std :: unordered_map < std :: string, int> . Обратите внимание, что std :: unordered_map < char, int> будет более эффективным, если требуется только один символ. std :: string позволяет подсчитывать сложные строки (например, map ["substring"] ++)
К картам можно обращаться, используя скобочные обозначения (например, map [index]), и, таким образом, можно эффективно устранить необходимость использования операторов if .
#include <string>
#include <unordered_map>
#include <iostream>
int main()
{
std::unordered_map< std::string, int > map = { {"a",0} };
map["a"] += 1;
std::cout << map["a"];
}
Что вы хотите:
const char char_offset = 'a';
const int num_chars = 26;
std::vector<int> amounts(num_chars, 0);
std::string word = "blahblah";
for (auto c : word) {
int i = c - char_offset;
// this if statement is only for range checking.
// you can remove it if you are sure about the data range.
if (i >= 0 && i < num_chars) {
++amounts[i];
}
}
for (int i = 0; i < (int)amounts.size(); ++i) {
std::cout << (char)(char_offset + i) << ": " << amounts[i] << std::endl;
}
<час> Output
a: 2
b: 2
c: 0
d: 0
e: 0
f: 0
g: 0
h: 2
i: 0
j: 0
k: 0
l: 2
m: 0
n: 0
...
Учитывая ваш пример, предполагая, что вся строка состоит из строчных букв и допустимых символов, существует довольно простое решение (то есть вы обрабатываете проверку)
for (int i = 0; i < word.size(); i++) {
amounts[word[i]-'a']++; // you can also do a pre-increment if you want
}