The key is that you want to work at the ID level. If the view you are creating is using ID, then you might consider using simple aggregate or table calculations.

But more likely, if you want the flexibility to work at any level of detail in the view, then use a Level of Detail calculation fixed to the ID level. The code would be something like:

{FIXED [ID] : MAX([Category])}

What this will do is return the maximum category for each ID. If it is only A then the MAX is A. If it is A and B then the max is B.

Be aware that FIXED level of detail calculations are evaluated across the entire context of the data (usually the entire data set unless you use a context filter). This means that even if you filter to a subset of data (e.g. to a certain date) where the Category is only A, but B exists within the entire context you'll still get B as the result.

You're welcome! If you are concerned that MAX or MIN might fail based on data, then you can get more specific with the calculation:

{FIXED [ID] : MAX(IF [Category] = "B" THEN "B" END)}

will return B for any category (at the row level) that has a B record in the data, even if there is an A record or C record (thus B wouldn't be the overall MAX). If there's not a B record in the data from the category, then the result will be NULL, so you can extend the calculation to then give you the "A" value if the "B" is null: