5

well basically it should list all the vector coords in this kind of format :

(x, y, z)

but at the moment it does like this (x, y, z, )

easiest way would be using if in the for cycle, but can i substract a small piece of string from the out variable?

my code:

    template <unsigned short m>
    std::ostream& operator<<(std::ostream& out, const Vector<m>& v) {
    out << "(";
    for(int i = 0; i < m; i++) {
        out << v.coords[i] << ", ";
    }
    out << ")";
    return out;
}
Jaanus
  • 16,161
  • 49
  • 147
  • 202
  • Why is the if statement not an option, just curious? – jonsca Mar 13 '11 at 09:18
  • I suppose Jaanus is trying to make the loop tighter for performance reasons. In this case it's not worth it though, the time spent doing the IO will far out weigh the time spending testing the loop index. It would be interesting to see if the compiler can optimize out the test in any case. – Himadri Choudhury May 26 '11 at 16:20

5 Answers5

21

This is from an old code base of mine. On the upside: it comes with unit tests:

Updated for modern times, more generic and self contained Live On Coliru

/*! note: delimiter cannot contain NUL characters
 */
template <typename Range, typename Value = typename Range::value_type>
std::string Join(Range const& elements, const char *const delimiter) {
    std::ostringstream os;
    auto b = begin(elements), e = end(elements);

    if (b != e) {
        std::copy(b, prev(e), std::ostream_iterator<Value>(os, delimiter));
        b = prev(e);
    }
    if (b != e) {
        os << *b;
    }

    return os.str();
}

/*! note: imput is assumed to not contain NUL characters
 */
template <typename Input, typename Output, typename Value = typename Output::value_type>
void Split(char delimiter, Output &output, Input const& input) {
    using namespace std;
    for (auto cur = begin(input), beg = cur; ; ++cur) {
        if (cur == end(input) || *cur == delimiter || !*cur) {
            output.insert(output.end(), Value(beg, cur));
            if (cur == end(input) || !*cur)
                break;
            else
                beg = next(cur);
        }
    }
}

And some corresponding unit test cases:

void testSplit() {
    std::vector<std::string> res;
    const std::string test = "a test ,string, to,,,be, split,\"up,up\",";
    TextUtils::Split(',', res, test);

    UT_EQUAL(10u, res.size());
    UT_EQUAL("a test ", res[0]);
    UT_EQUAL("string", res[1]);
    UT_EQUAL(" to", res[2]);
    UT_EQUAL("", res[3]);
    UT_EQUAL("", res[4]);
    UT_EQUAL("be", res[5]);
    UT_EQUAL(" split", res[6]);
    UT_EQUAL("\"up", res[7]); // Thus making 'split' unusable for parsing
    UT_EQUAL("up\"", res[8]); //  csv files...
    UT_EQUAL("", res[9]);

    TextUtils::Split('.', res, "dossier_id");
    UT_EQUAL(11u, res.size());

    res.clear();
    UT_EQUAL(0u, res.size());

    TextUtils::Split('.', res, "dossier_id");
    UT_EQUAL(1u, res.size());
    std::string UseName = res[res.size() - 1];
    UT_EQUAL("dossier_id", UseName);
}

void testJoin() {
    std::string elements[] = { "aap", "noot", "mies" };

    typedef std::vector<std::string> strings;

    UT_EQUAL(""               , TextUtils::Join(strings(), ""));
    UT_EQUAL(""               , TextUtils::Join(strings(), "bla"));
    UT_EQUAL("aap"            , TextUtils::Join(strings(elements, elements + 1), ""));
    UT_EQUAL("aap"            , TextUtils::Join(strings(elements, elements + 1), "#"));
    UT_EQUAL("aap"            , TextUtils::Join(strings(elements, elements + 1), "##"));
    UT_EQUAL("aapnoot"        , TextUtils::Join(strings(elements, elements + 2), ""));
    UT_EQUAL("aap#noot"       , TextUtils::Join(strings(elements, elements + 2), "#"));
    UT_EQUAL("aap##noot"      , TextUtils::Join(strings(elements, elements + 2), "##"));
    UT_EQUAL("aapnootmies"    , TextUtils::Join(strings(elements, elements + 3), ""));
    UT_EQUAL("aap#noot#mies"  , TextUtils::Join(strings(elements, elements + 3), "#"));
    UT_EQUAL("aap##noot##mies", TextUtils::Join(strings(elements, elements + 3), "##"));
    UT_EQUAL("aap  noot  mies", TextUtils::Join(strings(elements, elements + 3), "  "));

    UT_EQUAL("aapnootmies"    , TextUtils::Join(strings(elements, elements + 3), "\0"));
    UT_EQUAL("aapnootmies"    , TextUtils::Join(strings(elements, elements + 3), std::string("\0" , 1).c_str()));
    UT_EQUAL("aapnootmies"    , TextUtils::Join(strings(elements, elements + 3), std::string("\0+", 2).c_str()));
    UT_EQUAL("aap+noot+mies"  , TextUtils::Join(strings(elements, elements + 3), std::string("+\0", 2).c_str()));
}

See it Live On Coliru

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Works flawlessly. Super awesome. Thanks. – Yash Aug 22 '15 at 14:46
  • `added` is an unused var – Ian Jan 13 '19 at 17:44
  • @Ian Oh my. That's a long time ago. Good spot, and none of that code would pass my own code review today. I updated the answer to be a little bit better :) – sehe Jan 13 '19 at 21:22
  • Please don't rewrite answers so substantially so much later, but rather post a new answer. It's not fair on the 15 people who voted on the old version! – Lightness Races in Orbit Jan 13 '19 at 23:00
  • @LightnessRacesinOrbit in fairness they did so in the past, when the answer was slightly less outdated as well. And in my defense, I made sure all the test cases still pass (and you can now easily verify this, as opposed to the old situation). Cheers :) – sehe Jan 14 '19 at 00:21
  • In `Join`, I don't get the second check for `(b != e)`. If you just set `b = prev(e)`, then `b` is never `e`. In my eyes you could just replace `b = prev(e)` by `os << *prev(e)` and leave out the second if statement. (Also `using namespace std;` really?) – JHBonarius Jul 06 '20 at 10:52
5

Use an if statement to add the comma

for(int i = 0;i<m;i++)
{
  out<<V.coords[i];

  if(i !=m-1)
     out<<",";

}
jonsca
  • 10,218
  • 26
  • 54
  • 62
3

That isn't possible. Another possibility: moving out of the loop the output of the first or the last coordinates. Then there is no need of an if (or the operator ?:) inside the loop, but handling an empty vector is more complex as it will need an if outside the loop.

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
2

Loop from i to m-1 printing the value and a comma, then at the end of the loop (where you print out the ")"), print out the last element without a comma

deek0146
  • 962
  • 6
  • 20
0

How about this one:

template <unsigned short m>
std::ostream& operator<<(std::ostream& out, const Vector<m>& v) {
   std::string sep;
   out << "(";
   for(int i = 0; i < m; i++) {
      out << sep << v.coords[i];
      sep = ", ";
   }
   out << ")";
   return out;
}

You get no if conditions, but at small extra cost of using string reassignments.

Shriram V
  • 1,500
  • 2
  • 11
  • 20