0

I have the code below:

#include <iostream>
#include <list>
using namespace std;

class YTchannel{
public: 
    string name;
    string owner;
    int subs;
    list<string> video_title;
};

int main(){

    YTchannel ytc; 
    ytc.video_title={"A", "B", "C"};
    for(string videotitle: ytc.video_title){
        for(int i=1;i<=videotitle.size();i++){
            cout<<i<<videotitle<<endl;
            break;
        }
    }

I want to display the list of video titles with their respective number: 1A 2B 3C

But if I run the code, i'll obtain: 1A 1B 1C

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
incognito
  • 5
  • 2
  • 1
    Why did you write a nested loop? And why are you using a `list` instead of a `vector`? – UnholySheep Feb 06 '23 at 09:17
  • Well, pretty obvious: The inner loop iterates over the length of the titles... Try inserting a title longer than just a single character like `"ABC"` and you'll notice: `1ABC 2ABC 3ABC` (along with possibly other output if you leave them in the list. – Aconcagua Feb 06 '23 at 09:20
  • Why is this tagged as [tag:oop]? I don't really see any connection to OOP. – Konrad Rudolph Feb 06 '23 at 09:21
  • 2
    Note that unless you have specific requirements to use a list, the default container in C++ should almost always be `std::vector`. – Some programmer dude Feb 06 '23 at 09:22
  • A thing you should read about [`using namespace std`](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – before that bad practice gets a bad habit ;) – Aconcagua Feb 06 '23 at 09:58
  • 1
    Also, having an unconditional `break` inside a for-loop makes sure it only runs one round. Just like if there was no loop... – BoP Feb 06 '23 at 10:02
  • Using `auto` keyword makes your code better maintainable, consider you need to exchange `std::string` with some other type; you won't need to adjust your loop then: `for(auto videotitle : tyc.video_title)` – Aconcagua Feb 06 '23 at 10:09

3 Answers3

4

The frist loop:

for(string videotitle: ytc.video_title)

iterates over all the strings in the list.

The second loop:

for(int i=1;i<=videotitle.size();i++)

iterates over all characters in the current string. And in that loop you print the whole string each iteration.

The simple solution is to keep a separate counter that you increase in the one single first loop:

unsigned counter = 1;
for (auto const& title : yts.video_titles)
{
    std::cout << counter++ << ' ' << title << '\n';
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    I like the C++20 variant from the other answer – even though not (yet?) correct – for not requiring a variable visible outside the loop body; would recommend `size_t` as the correct type (matching the `size()` return value of containers) – inspite of `unsigned` not matching needs being unrealistic ;) – Aconcagua Feb 06 '23 at 09:35
1

You have a 'break' in your loop so you never increment the counter.

Additionally, in C++20 you can narrow the scope, by using the init statement in range-based loop.

#include <iostream>
#include <list>
using namespace std;

class YTchannel{
public: 
    string name;
    string owner;
    int subs;
    list<string> video_title;
};

int main(){

    YTchannel ytc; 
    ytc.video_title={"A", "B", "C"};
    int counter = 0; 
    for(string videotitle : ytc.video_title){
        cout<<++counter<<videotitle<<endl;
    }

    // C++20
   //YTchannel ytc; 
   //ytc.video_title={"A", "B", "C"};
   //for(int counter = 0; string videotitle : ytc.video_title){
   //   cout<<++counter<<videotitle<<endl;
   //}
}
bielu000
  • 1,826
  • 3
  • 21
  • 45
  • Yes I updated the answer before you made your comment. As for the references and so on - it's just a matter of use case. Maybe somebody wants to make a copy instead of taking a reference. OP is obviously a beginner and you're probably confusing him by introducing references that he probably does not know. DO NOT overcomplicate. – bielu000 Feb 06 '23 at 09:32
  • 1
    @bielu000 not sure I agree that insisting on good coding style from the beginning is overcomplicating things. – DividedByZero Feb 06 '23 at 09:38
  • Good coding style is not what a beginner should be interested in. Period. I've worked with students and they are usually scared of C++ precisely by pushing on them too many things at the same time, they don't understand. There will be a time when a beginner will learn what the reference is and how to use it and how it affects performance. – bielu000 Feb 06 '23 at 09:41
  • Have quite some difficulties in agreeing on as my experience is rather that it's hard to drop bad habits once they have already been acquired, a thing one doesn't have to go through if one learns the ropes right at the beginning. If references indeed are still unknown – considering their importance – then I'd say they are going to be introduced definitely too lately in that course. Admitted, a fault potentially problematic to fix here :( – Aconcagua Feb 06 '23 at 09:56
0

You're printing the string ("A", "B", or "C")'s length - not the list's. You want:

    ytc.video_title={"A", "B", "C"};
    auto it = ytc.begin()
    for(size_t i =0; i < ytc.size(); ++i){
        cout<<i<<*it++<<endl;
    }

However what you probably really want is:

#include <iostream>
#include <vector>
#include <algorithm>

// Include the headers for std::string
#include <string>

// Don't use 'using namespace std'

// Structs are all public by default, so they're a better choice than a class here
struct YTchannel{
    std::string name;
    std::string owner;
    int subs;
    // Vector is a better choice than list unless you know why it isn't
    std::vector<string> video_title;
};

int main(){
    YTchannel ytc; 
    ytc.video_title={"A", "B", "C"};

    for(auto it = ytc.begin(); it != ytc.end(); ++it){
        std::cout << std::distance(ytc.begin(), it) << videotitle << std::endl;
}
DividedByZero
  • 4,333
  • 2
  • 19
  • 33
  • I personally would prefer: `for(auto it = begin(); it != end(); ++it) { std::cout << it - begin() << ...; }` to avoid an additional variable visible outside the loop. Although apparently used less the iterator loop is more idiomatic anyway (or why is the range-based for loop defined int terms of otherwise?). Of course requires switching to `std::vector`, but that's recommendable anyway. – Aconcagua Feb 06 '23 at 09:24
  • @Aconcagua I agree - and I'm not sure I agree it's used less anyway. But in this case the most trivial implementation is probably the one that iterates over indices, not iterators. My *preferred* solution, is of course to just use iterators, std::vector, and std::distance instead of having a counter at all :) – DividedByZero Feb 06 '23 at 09:36
  • Well, counter + ranged-based for loop (especially as in the C++20 variant, see other answers) is still an attractive alternative – and if there are reasons to retain the list (e.g. for requiring pointers into, which might invalidate with `std::vector`) then this would definitely be the way I'd go ;) – with the list being retained `std::distance` would require iterating internally – turning printing invisibly into an `O(n²)` algorithm :( – Aconcagua Feb 06 '23 at 09:39
  • Oh, by the way: `for(auto& s : vector) { auto distance = &s - &vector.front(); }` would be yet an alternative to allow the range based for loop without additional variable, though pointer arithmetic contained might over-stress question author ;) – Aconcagua Feb 06 '23 at 09:43
  • @Aconcagua if the list is required, I agree. But what we probably have here is a Java programmer who picked the wrong container :) (also, if you need iterator validity guarantees, use a deque) – DividedByZero Feb 06 '23 at 09:43
  • `std::deque` has weaker guarantees if you erase in the middle! See https://en.cppreference.com/w/cpp/container/deque#Iterator_invalidation – Aconcagua Feb 06 '23 at 09:48