std::transform how to not return (and not throw), just skip?
I want to display folder contents displaying (not having any folder system) So Having a std::set<string_file_names>
with std::strings
in it and some given path to some dir we want to search for folder contents as in normal fs. so having set of:
set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");
and search string "bin/obj/Debug/"
we want to get 3 Items - folder, 2 files:
CloudServerPrototype/
vc100.idb
vc100.pdb
But we get also an empty line. How to not get it and how to throw an error in case of non items found?
Entire code:
#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <iterator>
using namespace std;
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s开发者_高级运维)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length();
}
else
{
return "";
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
};
void directory_listning_without_directories_demo()
{
set<string> output;
set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");
std::transform(demo_set.begin(),
demo_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug/"));
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
int main()
{
directory_listning_without_directories_demo();
cin.get();
return 0;
}
code sample based on this grate answer.
As there is no transform_if, a proper way to do this would be to to first copy_if the paths that have a non-zero-length get_pertinent_part result into another container, and then run the transform on that new container.
Alternatively, you could write something like transform_if that compares the result of your transform function with a predicate. Here's my untested shot at it:
template <class InIt, class OutIt, class UnaryFunction, class Predicate>
void transform_if_value(InIt first, InIt last, OutIt out, UnaryFunction fn, Predicate pred)
{
for ( ; first != last; ++ first)
{
auto val = fn(*first);
if (pred(val))
*out++ = val;
}
}
Then you could use that like
transform_if_value(
demo_set.begin(),
demo_set.end(),
std::inserter(output, output.begin()),
get_pertinent_part("bin/obj/Debug/"),
[](const std::string& s) {return !s.empty();});
For even cooler syntax, check out boost's Range adaptors:
boost::copy(
demo_set | boost::adaptors::transformed(get_pertinent_part("bin/obj/Debug/"))
| boost::adaptors::filtered([](const std::string& s) {return !s.empty();}),
std::inserter(output, output.begin()));
You can use std::accumulate
(not from <algorithm>
but from <numeric>
) to, well, accumulate the matches into your output.
std::set<string> const&
operator()(std::set<string> const& out, std::string const& candidate) const
{
if(/* same logic to check if candidate matches */) {
out.insert(/* insert processed/transformed path */);
}
return out;
}
Call as:
std::accumulate(
demo_set.begin(), demo_set.end()
, std::ref(output)
, get_pertinent_path("bin/obj/Debug/") );
Note the use of std::ref
(from <functional>
). You don't want the output set to be moved around (plus the result of the call to std::accumulate
is ignored so no changes will be visible). Alternatively, if you don't want a dependency on std::ref
, you can use pointers.
edit: heh, revisiting this idea, this is arguably no better than using std::for_each
and passing a reference to the ouput to the constructor of the functor. YMMV.
精彩评论