A little known fact about vampires is that they must drink the blood of victim that has a compatible donor blood type. The compatibility matrix for vampires is the same as the regular red blood cell donor/recipient matrix. This can be summarized by the following American Red Cross table

Type    You Can Give Blood To    You Can Receive Blood From
A+      A+, AB+                  A+, A-, O+, O-
O+      O+, A+, B+,AB+           O+, O-
B+      B+, AB+                  B+, B-, O+, O-
AB+     AB+                      everyone
A-      A+, A-, AB+, AB-         A-, O-
O-      everyone                 O-
B-      B+, B-, AB+, AB-         B-  O-
AB-     AB+, AB-                 AB-, A-, B-, O-


Write a function or program that takes a blood type as input and outputs two lists:

  1. the unordered list of types that may receive donation of the input type
  2. the unordered list of types that may give donation to the input type

If you write a function, then please also provide a test program to call that function with a few examples, so I can easily test it. In this case, the test program would not count towards your score.


Input must be a string representing exactly one of the 8 possible red blood cell types O− O+ A− A+ B− B+ AB− AB+. Input may be given via the normal methods (STDIN, command-line args, function args, etc).

If any other input is given then the program/function must return empty output or throw an error. Normally strict input-checking is not great in questions, but I felt given the life-death implications of getting blood types wrong that I should add this rule.


Output will be two human-readable lists of blood types in whatever format is suitable for your language. In the special cases where one of the output list contains all 8 types, this list may optionally be replaced with a single item list containing everyone.

Normal output will go to one of the normal places (STDOUT, function return, etc).

Other rules

  • Standard loopholes are banned
  • You may use whatever pre-existing 3rd party libraries you need, so long as they are not designed explicitly for this purpose.


  • For input AB-, the two output lists would be: {AB+, AB-}, {AB-, A-, B-, O-}
  • For input AB+, the two output lists would be: {AB+}, {O−, O+, A−, A+, B−, B+, AB−, AB+} or {AB+}, {everyone}

Personal note: Please consider donating blood if you are able to. Without the transfusion I received a few years ago, I might not be here today, so I feel very grateful towards those who are able to donate!

  • \$\begingroup\$ @MartinBüttner Actually I will accept both. Most likely the 2nd form will yield shorter code in most languages, but perhaps there will be some special case where using the first form could be shorter. \$\endgroup\$ Mar 19, 2015 at 19:14
  • 3
    \$\begingroup\$ Tangentially related - this brilliant answer worldbuilding.stackexchange.com/a/11203/2094 \$\endgroup\$ Mar 19, 2015 at 19:19
  • 1
    \$\begingroup\$ That fact is not so little-known. \$\endgroup\$ Mar 21, 2015 at 9:28
  • 1
    \$\begingroup\$ @leftaroundabout Thanks - A Bit of Fry and Laurie has always been a favourite of mine! \$\endgroup\$ Mar 23, 2015 at 0:09
  • 1
    \$\begingroup\$ A picky vampire, eh? Dracula is turning in his casket. Also, the title sounds like the name of a retired goth-rock-band. \$\endgroup\$ Jun 19, 2015 at 20:11

20 Answers 20


Clip, 69

*cTx\{fFx`Tf[tFtx}T`[Fx[y!VVx"O-"Vy"O-"}[TC"A+ B+ AB+ O+ A- B- AB- O-

Input: AB-

Output: {{"AB+", "AB-"}, {"A-", "B-", "AB-", "O-"}}


A blood type x can give to y if all of x's antigens are included in y. The program defines the function F as whether x can give to y, and T as the list of types.

*cTx                 .- If T contains x (the input)         -.
    \                .- Print                               -.
     {             ` .- a list of                           -.
      fFx`T          .- filter each t in T with F(x,t)      -.
           f[tFtx}T  .- filter each t in T with F(t,x)      -.

[Fx[y              } .- F is a function of x and y          -.
     !V              .- all letters of ... are included in ...   -.
       Vx"O-"        .- x, with O and - removed             -.
             Vy"O-"  .- y, with O and - removed             -. 

[TC"A+ B+ AB+ O+ A- B- AB- O-   .- T is the list of types -.

Java 8, 373

import java.util.*;void f(String s){List<String>l=new ArrayList(Arrays.asList("A+,B+,AB+,O+,A-,B-,AB-,O-".split(","))),m=new ArrayList(l);int i=l.contains(s)?1:0/0;l.removeIf(x->g(s,x)<1);m.removeIf(x->g(x,s)<1);System.out.print(l+","+m);}int g(String s,String t){for(char c:s.replaceAll("O|-","").toCharArray())if(!t.replaceAll("O|-","").contains(""+c))return 0;return 1;}


void f(String s) {
    List<String> l = new ArrayList(Arrays.asList("A+,B+,AB+,O+,A-,B-,AB-,O-".split(","))),
                 m = new ArrayList(l);
    int i = l.contains(s) ? 1 : 0 / 0;
    l.removeIf(x -> g(s, x) < 1);
    m.removeIf(x -> g(x, s) < 1);
    System.out.print(l + "," + m);

int g(String s, String t) {
    for (char c : s.replaceAll("O|-", "").toCharArray()) {
        if (!t.replaceAll("O|-", "").contains("" + c)) {
            return 0;
    return 1;

Run it here: http://repl.it/e98/1

Note that static had to be added to each method to call them from the main method.

  • 2
    \$\begingroup\$ I added a link to an easily-run program for you. Edit the string parameter inside the function call in the main method to see the other inputs' outputs. \$\endgroup\$
    – mbomb007
    Mar 19, 2015 at 21:02

Pyth, 61 59 50

L-{b"O-"M!-yGyHJmsd*c"A O B AB"d"+-"I}zJfgzTJfgYzJ

Run it here.


L-{b"O-"                         Create function y(b) that makes a set from b's 
                                 characters minus O and -.
M!-yGyH                          Create function g(G,H) that checks if y(G) is 
                                 a subset of y(H).
J                                Assign to J...
 msd                             The concatenation of every element in...
    *c"A O B AB"d"+-"            The Cartesian product of A O B AB and + -.
I}zJ                             If input in J then...
    fgzTJ                        Print all elements e in J if g(input, e).
    fgYzJ                        Print all elements e in J if g(e, input).
  • \$\begingroup\$ @user23013 Thanks for the edit. It definitely should've been Cartesian :) \$\endgroup\$
    – orlp
    Mar 20, 2015 at 14:31

CJam, 64 bytes


The m*:s part comes from Martin's CJam answer. (I didn't read the other parts yet.)

There will be still some serious problems because they'll be never sure about the order of the two lists. And Block ArrayList & might be implemented in later versions of CJam.


"AB"_a+'O+         " Generate ['A 'B \"AB\" 'O]. ";
"+-"m*:s:T         " Generate the list of blood types and store in T. ";
    Tqa#           " Find the input in T. ";
=                  " Find the blood type by the index.
                     If not found, return a block to cause an error. ";
a+                 " Append the verified input to T. ";
T4=                " AB+. ";
f&                 " Intersect each blood type with AB+. ";
_)f{\-!}           " Check emptiness of input - each item. ";
\)f-:!             " Check emptiness of each item - input. ";
]{                 " For both lists: ";
    T]z::*         " Replace items in T where there is a 0 with empty strings. ";
    La-            " Remove empty strings. ";
    p              " Print. ";

Prolog, 119 110 bytes

g(X,Y):-(X= -A;X=A),(Y= -B;Y=B),u(A,B).


  1. Blood types have the following properties: everytime you have a - (e.g. a-), you can give to the same people as the one who have positive equivalent of your group (e.g. a), as well as their negative counterpart (e.g. a gives to ab, so a- gives to ab and ab-). Based on this property, and abusing the notations a little bit to make use of the minus and plus operators, we can factor a lot of cases. Please tell me if you find it acceptable. If you prefer having the original (postfix) syntax, here is a non-golfed version:

  2. This is Prolog, so the interactive environment allows to query everything as requested (see example below). Granted, we don't have lists strictly as an output, but this is equivalent. We also naturally handle error cases as a consequence.


donors(X,L) :- findall(Y,g(Y,X),L).
receivers(X,L) :- findall(Y,g(X,Y),L).

test :-

Then, we execute test:

a : give_to([ab, a]) : receive_from([o, a])
o : give_to([a, b, ab, o]) : receive_from([o])
b : give_to([ab, b]) : receive_from([o, b])
ab : give_to([ab]) : receive_from([a, b, o, ab])
-(a) : give_to([+(ab), +(a), -(ab), -(a)]) : receive_from([-(o), -(a)])
-(o) : give_to([+(a), +(b), +(ab), +(o), -(a), -(b), -(ab), -(o)]) : receive_from([-(o)])
-(b) : give_to([+(ab), +(b), -(ab), -(b)]) : receive_from([-(o), -(b)])
-(ab) : give_to([+(ab), -(ab)]) : receive_from([-(a), -(b), -(o), -(ab)])

... which, without proper formatting, is the same matrix as the one given in the question.


The predicate g/2 is the give relationship: g(X,Y) means people of blood type X may give blood to people of blood type Y.

Find receivers for group a:

[eclipse]: g(a,R).    

R = ab
Yes (0.00s cpu, solution 1, maybe more) ? ;

R = a
Yes (0.00s cpu, solution 2)

Find receivers for orange_juice (should fail):

[eclipse] g(orange_juice,L).

No (0.00s cpu)

Find donors for O-:

[eclipse] g(X,-o).

X = -(o)
Yes (0.00s cpu)

Who can give what? :

[eclipse] g(X,Y).

.... 27 answers ....

We do not go into an infinite recursion loop (it was the case in preliminary tests).


Javascript, 167

p=function(t){o="";if((x=(l="O- O+ B- B+ A- A+ AB- AB+".split(" ")).indexOf(t))<0)return;n=2;while(n--){y=8;while(y--)if([y,x][n]-(y&x)==0)o+=" "+l[y];o+=";"}return o}


function p(btype){
    output = "";
    btypeList = "O- O+ B- B+ A- A+ AB- AB+".split(" ");

    btypeInt = btypeList.indexOf(btype);
    // thus we have the scheme
    // btypeInt = 0b(has A antigen)(has B antigen)(has rhesus antigen)

    if(btypeInt < 0) // i.e. broken blood type string

    for(receiving = 7; receiving >= 0; receiving--)
        if(giving - (receiving & btypeInt) == 0)
            // i.e. the receiving person has at least all the antigens of our donor
            output += " " + btypeList[receiving];

    output += ";";

    for(giving = 7; giving >= 0; giving--)
        if(btypeInt - (receiving & btypeInt) == 0)
            // i.e. the giving person has no antigens that our patient doesn't have
            output += " " + btypeList[receiving];

    return output;

testing function:

function tester(){
    btypeList = "O- O+ B- B+ A- A+ AB- AB+".split(" ");
    for(i=0; i<8; i++){
        console.log("Patient is " + btypeList[i])
    console.log("Erroneous blood type => returns void:")

Encoding the blood type in binary has the advantage that another antigen (e.g. the Kell antigen) is easily incorporated into the code by just adding another bit.

Donate blood in Zurich, CH: Blutspende Zürich

  • \$\begingroup\$ You can use "O-O+B-B+A-A+AB-AB+".match(/\w+\W/g) instead of "O- O+ B- B+ A- A+ AB- AB+".split(" ") to save 2 chars. \$\endgroup\$
    – Oriol
    Mar 19, 2015 at 23:21
  • \$\begingroup\$ Or you can save exactly the same by making the delimiter a number "O-1O+1B-1B+1A-1A+1AB-1AB+".split(1) and using => function should save some too. \$\endgroup\$
    – red-X
    Mar 20, 2015 at 10:28
  • \$\begingroup\$ Yes, but @Oriol's can be further shortened in the regular expression by 1 character: /\w+./g \$\endgroup\$
    – manatwork
    Mar 20, 2015 at 10:31
  • \$\begingroup\$ Always use for(;;) instead of while(). At least same length, but can be shorter. n=2;while(n--) => for(n=2;n--;) \$\endgroup\$
    – edc65
    Mar 20, 2015 at 10:59
  • \$\begingroup\$ All in all, very clever. Can be shortened to 147 using standard golf tricks: http://jsfiddle.net/j2hep8e8/2/ \$\endgroup\$
    – edc65
    Mar 20, 2015 at 11:52

CJam, 94 bytes

Wow, this is long... while I think I could probably golf this approach below 80, I think I might have done better by first computing the matrix and then simply picking out the correct row and column. Anyway, here it is:


Test it here.

I'll add an explanation when I'm done golfing.


Groovy, 115


The idea is to encode A, B and rhesus factor as one bit each. We can then inverse the bits to get all the antigens on the receiving side and use it to check that there is no corresponding antibodies on the giving side. This is more or less the same as the existing JavaScript solution.

Sample execution

groovy> println x("AB+") 
groovy> println x("AB-") 
groovy> println x("A+") 
groovy> println x("A-") 
groovy> println x("B+") 
groovy> println x("B-") 
groovy> println x("O+") 
groovy> println x("O-") 
groovy> println x("X") 

[[AB+], [O-, O+, B-, B+, A-, A+, AB-, AB+]]
[[AB-, AB+], [O-, B-, A-, AB-]]
[[A+, AB+], [O-, O+, A-, A+]]
[[A-, A+, AB-, AB+], [O-, A-]]
[[B+, AB+], [O-, O+, B-, B+]]
[[B-, B+, AB-, AB+], [O-, B-]]
[[O+, B+, A+, AB+], [O-, O+]]
[[O-, O+, B-, B+, A-, A+, AB-, AB+], [O-]]

Python, 187 bytes

Different approach:

def D(a,b):X=lambda c:c in a and 1-(c in b);return(X('A')+X('B')+X('+'))<1
T="O- O+ A- A+ B- B+ AB- AB+".split()
X=lambda t:(0,([u for u in T if D(t,u)],[u for u in T if D(u,t)]))[t in T]

Can probably be golfed a bit more.


for t in T + ["zz"]:
    print t, X(t)


O- (['O-', 'O+', 'A-', 'A+', 'B-', 'B+', 'AB-', 'AB+'], ['O-'])
O+ (['O+', 'A+', 'B+', 'AB+'], ['O-', 'O+'])
A- (['A-', 'A+', 'AB-', 'AB+'], ['O-', 'A-'])
A+ (['A+', 'AB+'], ['O-', 'O+', 'A-', 'A+'])
B- (['B-', 'B+', 'AB-', 'AB+'], ['O-', 'B-'])
B+ (['B+', 'AB+'], ['O-', 'O+', 'B-', 'B+'])
AB- (['AB-', 'AB+'], ['O-', 'A-', 'B-', 'AB-'])
AB+ (['AB+'], ['O-', 'O+', 'A-', 'A+', 'B-', 'B+', 'AB-', 'AB+'])
zz 0

Ruby, 237 232 223 221 210 207 bytes

Fixed some extraneous backslashes in the regular expressions and made it so it just prints out the lists as opposed to storing them to variables and then printing them. Sometimes you miss the obvious things when trying to golf!

o=->b{Regexp.new b.gsub(?O,?.).gsub(?+,'.?\\\+').gsub'-','.?(\W)'};b=gets.chop;t=["A+","B+","AB+","O+","A-","B-","AB-","O-"];exit if !t.include? b;p t.reject{|x|!x.match o.(b)};p t.reject{|x|!b.match o.(x)}


types = ["A+","A-","B+","B-","AB+","AB-","O+","O-"];
exit if !types.include?(b);
regex1 = Regexp.new b.gsub("O",".").gsub('+','.?\\\+').gsub('-','.?(\\\+|\\\-)')
donate = types.reject {|x|!x.match(regex1)};
p donate;
receive = types.reject {|x| regex2 = Regexp.new x.gsub("O",".").gsub('+','.?\\\+').gsub('-','.?(\\\+|\\\-)'); !b.match(regex2)};
p receive;

Basically, I construct a custom regular expression for the blood type entered to check if you can donate to another blood type. I then iterate through the blood types and apply that same regex to them and check if they can donate to the one specified.

This can probably be golfed down even more. This is my first time attempting code golf, heh.


Python 2, 168 bytes

This is the same method as Blackhole's answer. Exits with an error if the parameter is not found in the list of types.

def f(t):l='A+ O+ B+ AB+ A- O- B- AB-'.split();c=[9,15,12,8,153,255,204,136];i=l.index(t);print[s for s in l if c[i]&1<<l.index(s)],[s for s in l if c[l.index(s)]&1<<i]

Less golfed:

def f(t):
    l = 'A+ O+ B+ AB+ A- O- B- AB-'.split()
    c = [9, 15, 12, 8, 153, 255, 204, 136]
    i = l.index(t)
    x = [s for s in l if c[i] & 1 << l.index(s)]
    y = [s for s in l if c[l.index(s)] & 1 << i]
    print x, y

Run it here: http://repl.it/eaB

I also tried a couple other slight changes, but couldn't get it any shorter...

#172 bytes
def f(t):l='A+ O+ B+ AB+ A- O- B- AB-'.split();c=[9,15,12,8,153,255,204,136];a=lambda x:l.index(x);i=a(t);print[s for s in l if c[i]&1<<a(s)],[s for s in l if c[a(s)]&1<<i]

#171 bytes
def f(t):l='A+ O+ B+ AB+ A- O- B- AB-'.split();c=[9,15,12,8,153,255,204,136];a=lambda x:l.index(x);print[s for s in l if c[a(t)]&1<<a(s)],[s for s in l if c[a(s)]&1<<a(t)]

PHP (287 bytes):

Yeah, this is pretty long, but it works as expected.

It may be possible to shorten a lot:

!preg_match('@^(AB?|B|O)[+-]$@',$t=$_GET[T])&&die("$t invalid");$S=array_flip($D=split(0,'O+0A+0B+0AB+0O-0A-0B-0AB-'));$L=[[1230,40],[13,1504],[23,2604],[3,$r=12345670],[$r,4],[1537,54],[2637,64],[37,7564]];for($j=0;$j<2;){foreach(str_split($L[$S[$t]][$j++])as$v)echo$D[$v].' ';echo'

This is not easy to read and was't easy to write.

It works as intended, outputting the ones you can give to and the ones you can receive from on another line.

This requires a URL parameter T= with the type.


CJam, 80 bytes

This is still too long. Probably I can shave off 4 to 5 bytes more.


For any invalid input, either prints an empty array, or throws an error.

Try it online here or run the whole test suite

  • \$\begingroup\$ Is the XKCD in the begging intended? \$\endgroup\$
    – Ypnypn
    Mar 20, 2015 at 19:33
  • \$\begingroup\$ @Ypnypn begging ? I did not intend to initially, but it turned out like that. Maybe the world is trying to tell us something... \$\endgroup\$
    – Optimizer
    Mar 20, 2015 at 20:21
  • \$\begingroup\$ Sorry, I meant beginning. \$\endgroup\$
    – Ypnypn
    Mar 22, 2015 at 19:17

APL, 66


Try it here.

  • \$\begingroup\$ Maybe that's 66 characters, but definitely not 66 bytes. The question doesn't say what's used to score though. \$\endgroup\$
    – orlp
    Mar 20, 2015 at 11:58
  • 1
    \$\begingroup\$ @orlp code-golf is scored in bytes by default (see the tag wiki). But it is said there is an APL code page where one character is one byte. I don't know exactly which APL code page is used nowadays, though. \$\endgroup\$
    – jimmy23013
    Mar 20, 2015 at 12:06
  • \$\begingroup\$ @orlp "bytes", but not "UTF-8 bytes". Here is a code page containing all of these characters. \$\endgroup\$ Mar 20, 2015 at 19:39

C, 224

#define d(x)for(i=0;i<8;i++)if((i x j)==j)printf("%s%s%s%c ",i&2?"A":"",i&4?"B":"",i&6?"":"0","-+"[i&1]);puts("");
main(i,j){char d[4],*c=&d;scanf("%s",c);j=(c[2]?c++,2:0)+(c[1]-'+'?0:1)+(*c>='A'?2:0)+(*c>'A'?2:0);d(&)d(|)}

De-golfed it shows:

/* j = 1*(has+) + 2*(hasA) + 4*(hasB) */
#define d(x) for(i=0;i<8;i++) \
                 if((i x j)==j) \
                      printf("%s%s%s%c ",i&2?"A":"",i&4?"B":"",i&6?"":"0","-+"[i&1]); \

    char d[4], *c=&d;

    j= (c[2]? (c++,2):0)            /* ABx */
            + (c[1]-'+'?0:1)
            + (c[0]>='A'?2:0)
            + (c[0]>'A'?2:0);

    // print possible receipients, and then donators

PHP - 215 212 206 bytes

function($t){$c=[9,15,12,8,153,255,204,136];if(($a=array_search($t,$l=split(1,'A+1O+1B+1AB+1A-1O-1B-1AB-')))===!1)die;foreach($l as$k=>$v){if($c[$a]&1<<$k)$x[]=$v;if($c[$k]&1<<$a)$y[]=$v;}var_dump($x,$y);}

Here is the ungolfed version:

function ($type)
    $typesList = ['A+', 'O+', 'B+', 'AB+', 'A-', 'O-', 'B-', 'AB-'];
    $donationCompatibilityList = [

    $idType = array_search($type, $typesList);
    if ($idType === false) {

    $canGiveToList = [];
    $canReceiveFromList = [];
    foreach ($typesList as $currentIdType => $currentType)
        if ($donationCompatibilityList[$idType] & 1 << $currentIdType ) {
            $canGiveToList[] = $currentType;

        if ($donationCompatibilityList[$currentIdType ] & 1 << $idType) {
            $canReceiveFromList[] = $currentType;

    var_dump($canGiveToList, $canReceiveFromList);

Thanks to manatwork for saving 4 bytes.

  • \$\begingroup\$ The split by integer trick works in PHP too: explode(1,'A+1O+1B+1AB+1A-1O-1B-1AB-'). And as we not necessarily keep up with nice coding habits, sometimes we use deprecated features, like the split() function. \$\endgroup\$
    – manatwork
    Mar 20, 2015 at 11:16
  • \$\begingroup\$ @manatwork Well spotted! I've edited my answer, thanks. \$\endgroup\$
    – Blackhole
    Mar 20, 2015 at 21:31

Perl, 107 112

Finally encoding the type names in numbers gave the shorter code.

#!perl -p

Older version

#!perl -p

Pyth, 58

Partly the same as orlp's solution, but somewhat different and completely self-designed.

M&n+eGeH"+-"!f!}T+H\OPGJsm,+d\++d\-c"O A B AB"dfgzYJfgZzJJ


M                          create a function g(G,H) that returns
  n+eGeH"+-"                 G[-1] + H[-1] is not "+-"
 &                          and
            !f!}T+H\OPG      chars of G[:-1] not in H + "O" is falsy (empty)
J                          J =
 s                          merge
  m                          map
   ,+d\++d\-                  lambda d: (d + "+", d + "-")
            c"O A B AB"d      over ["O", "A", "B", "AB"]
fgzYJ                      print all J's items x where g(input, x)
fgZzJ                      print all J's items x where g(x, input)

J, 120 bytes

   f=.3 :';"1(2 8$,(s-:"*<y,'' '')#8 2 8$#:213472854600871062656691437010712264449x)#s=.<;.2''A+ B+ AB+ O+ A- B- AB- O- '''

   f 'A+'
A+ AB+      
A+ O+ A- O- 

   f 'AB-'
AB+ AB-      
A- B- AB- O- 

The function fails on invalid inputs. The big decimal number is the encoding of the full compatibility matrix.

(Very long solution for multiple reasons.)

Try it online here.


05AB1E, 29 bytes


Try it online!


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.