Forum Replies Created
-
AuthorPosts
-
::
Thanks for the hint! I tried using
static_cast<double>(e)
directly, but that fails because std::string cannot be converted to double at compile time. So, I extended your idea using my previous idea of checking at compile time the type:#include <iostream>
#include <vector>
#include <variant>
#include <string>
#include <algorithm>
#include <set>
#include <ranges>
#include <limits>
using numberType = std::variant<int, float, double, std::string>;
template <typename T>
void printArray(const std::vector<T>& arr) {
for (const auto& element : arr) {
std::visit([](auto&& value) {
std::cout << value << " ";
}, element);
}
std::cout << std::endl; // Print a newline after printing all elements
}
int main() {
std::vector<numberType> arr = {5, 8, "apple", 3.2, 6, 2, 3, 7.8f, "4.7", 5, "10", 3.2};
std::cout << "Unsorted array with mixed types and duplicates: ";
printArray(arr);
// Use std::set to ensure uniqueness
std::set<numberType> uniqueSet(arr.begin(), arr.end());
// Convert the set back to a vector
std::vector<numberType> uniqueArr(uniqueSet.begin(), uniqueSet.end());
std::cout << "Unsorted array with mixed types and no duplicates: ";
printArray(uniqueArr);
// Here we use std::ranges::sort to operate on a range with a projection lambda
std::ranges::sort(uniqueArr, std::less(), [](auto const& x) {
// apply a lambda function as a visitor to handle the different types contained in the std::variant x
// the result of the lambda must be double
return std::visit([](auto const& e) -> double {
// compile-time conditional branching to check if two types are the same
// use std::decay_t to ensure the type will always be the basic one
if constexpr (std::is_same_v<std::decay_t<decltype(e)>, std::string>) {
try { // if e is a std::string try if it can be automatically converted to a double
return std::stod(e); // Convert numeric strings to double
} catch (...) { // otherwise place it at the end of the sorted array
return std::numeric_limits<double>::max(); // Non-numeric strings go to the end
}
} else { // other types can be casted directly
return static_cast<double>(e); // For numeric types
}
}, x);
});
std::cout << "Sorted array with mixed types: ";
printArray(uniqueArr);
return 0;
}
Output:
Unsorted array with mixed types and duplicates: 5 8 apple 3.2 6 2 3 7.8 4.7 5 10 3.2
Unsorted array with mixed types and no duplicates: 2 3 5 6 8 7.8 3.2 10 4.7 apple
Sorted array with mixed types: 2 3 3.2 4.7 5 6 7.8 8 10 apple
This is of course a very elegant solution, but it doesn’t solve the original problem completely. For example, if I were to define my array as follows,
std::set
won’t consider 3.2 and “3.2” as the same element:std::vector<numberType> arr = {5, 8, "apple", 3.2, 6, 2, 3, 7.8f, "4.7", 5, "10", "3.2"}
. Moreover, I don’t like the fact that std::set is not preserving the original order of the elements of the input array. In fact, I’d like to have5 8 apple 3.2 6 2 3 7.8 4.7 10
.By using again the
struct Compare
I could handle the problem by defining a custom operator forstd::set
. But the code would be tedious and ugly. Any suggestion and/or improvements on the implementation above? -
AuthorPosts