[Question]
  [
# Sort the Textbooks
School is starting soon (if it hasn't already) and so it's time to get our textbooks in order. You need to sort your books in alphabetical order but that takes too long so you decide to write a program to do it.
### Examples
**Input:**
```
 _
| |  _
|F| | |
|o|_|P|
|o|B|P|
| |a|C|
| |r|G|
|_|_|_|
```
**Output:**
```
   _
  | |_
  |F| | 
 _|o|P|
|B|o|P|
|a| |C|
|r| |G|
|_|_|_|
```
---
### Input
The input will be a set of books which need to rearranged alphabetically. It will contain only: `|`, `_`, , and `A-Za-z`. The titles of the books are read vertically, top-bottom.
You may choose to assume the input is padded with whitespace to fit a rectangle. If you choose to have your input padded with whitespace, please specify this in your answer.
The very maximum book height your program will need to handle is 5,120 lines tall without failing.
The books will always be 1-thick and their will always be at least one book in the input
### Output
The output will need to be the same set of books organized in alphabetical order. The height of the books must stay the same and the title must have the same spacing from the top when re-arranged.
Books should be sorted alphabetically. If your language sports a sort function you can use that. Otherwise you can use alphabetical sorting as described [here](https://en.wikipedia.org/wiki/Alphabetical_order).
**Book Title Examples**
```
 _
| |
| |
|F|
|o|
|o|
| |
| |
|B|
|a|
|r|
| |
| |
|_|
```
This books title is:
```
"Foo  Bar"
```
Book titles will *only* contain letters and spaces.
Trailing whitespace is allowed
---
# Winning
This is [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'") so shortest code in bytes wins. 
  
[Answer]
# Python 3, 231 bytes
```
def f(s):
 *M,L=sorted(["".join(c).strip()for c in zip(*s.split("\n"))][1::2],key=lambda x:x[1:-1].strip()),;l=m=0
 for r in L+[""]:n=len(r);M+="|"*~-max(n,l),r;m=max(n,m);l=n
 for r in zip(*[x.rjust(m)for x in M]):print(*r,sep="")
```
Just a quick hack. Zip the books, sort, rezip, taking care of the columns of `|` while we're at it.
Input a multiline string, padded with trailing spaces to a rectangle. The output has one more trailing space on each line than necessary.
## Ungolfed
```
def f(s):
  new_cols = []
  # Zip columns, removing the spaces above each book
  # [1::2] is to skip columns of |s, keeping only the books
  books = ["".join(c).strip() for c in zip(*s.split("\n"))][1::2]
  # Sort based on title, [1:-1] to remove the top and bottom _s
  books.sort(key=lambda x:x[1:-1].strip())
  last = 0
  max_height = 0
  for book in (books + [""]):
    height = len(book)
    # Append |s as necessary for the left edge of the current book
    # The +[""] above is for the right edge of the last book
    new_cols.extend(["|"*(max(height, last) - 1), book])
    max_height = max(height, max_height)
    last = height
  # Rezip columns, add back spaces as necessary and print
  for col in zip(*[x.rjust(max_height) for x in new_cols]):
      print("".join(col))
```
[Answer]
## Ruby (~~209~~ ~~204~~ ~~200~~ 198 bytes)
```
a=n.tr(?|,' ').split$/
i=!p;t=a.map(&:chars).transpose.map(&:join).select{i^=a}.sort_by{|s|s[/[A-Z]/][0]}
x=0;t.map{|t|y=0;u=p;t.chars{|c|u&&a[y][x,3]=?|*3;a[y][x+1]=c;y+=1;u|=c=='_'};x+=2}
a.join$/
```
The `transpose` function in this solution requires that all lines are the same length, hence the input needs to be padded with whitespace.
### Explanation
```
def sort_books(n)
  a = n.tr(?|,' ')  # pre-emptively remove all the '|'.
    .split $/         # and split into an array of lines
                      # ($/ is the INPUT_RECORD_SEPARATOR, typically "\n")
                      # we're going to write our answer into `a` later
  i = !p # i = true; we'll use this as a flip-flop variable
         # Kernel#p returns nil with no args
  # we're now going to get a sorted array of book titles (t)
  t = a.map(&:chars)  # break array into nested array of every character
       .transpose     # and transpose the entire array
       .map(&:join)   # this gives us an array of "horizontal" book titles with dividers
       .select { i ^= a } # select every second line
                          # (i.e. just titles without dividers)
                          # `i` starts off true
                          # `a` is truish (it's our original array)
                          # `^=` is the bitwise xor assignment,
                          #      it will alternate true/false on each execution
       .sort_by { |s| s[/[A-Z]/][0] } # sort by the first alphabetical char
  # use counters for less chars than `each_with_index`
  # x and y are cartesian coordinates in the final array
  x = 0 # start in the left-hand column
  # go through each title
  t.map { |t|
    y = 0 # each book title starts on the top row
    u = p # `u` is "have we reached the book's spine yet?" (or are we above it?)
          # `u` starts off false and we'll set it true when we see the first '_'
          # after which we'll start writing the book's edges
    # go through each character of each title, including leading spaces and '_'s
    # this will "descend" down the array writing each letter of the title
    # along with the "edges"
    t.chars { |c|
      u &&                  # if we're on the spine
        a[y][x,3] = ?|*3;   # write ||| in the next 3 columns
                            # the middle | will be overwriten by the title char
      a[y][x+1] = c; # write the current title char into the second (x+1) column
      y+=1; # descend to the next row
      u |= c == '_' # Since '_' is the top and bottom of the book,
                    # this toggles whether we're on the spine
    }
    x += 2 # jump to the right 2 columns and start on the next title
  }
  a.join $/ # hopefully this is obvious
end
```
[Answer]
# CJam, 60 bytes
```
qN/:Kz1>2%{_{" _"-}#>}$_{_'_#>,}%2,\*2ew{:e>('|*K,Se[}%.\zN*
```
I tried porting my Python answer, which is also similar to [@RetoKoradi's approach](https://codegolf.stackexchange.com/a/54307/21487).
[Try it online](http://cjam.aditsu.net/#code=qN%2F%3AKz1%3E2%25%7B_%7B%22%20_%22-%7D%23%3E%7D%24_%7B_%27_%23%3E%2C%7D%252%2C%5C*2ew%7B%3Ae%3E%28%27%7C*K%2CSe%5B%7D%25.%5CzN*&input=%20%20%20%20%20%20%20%20%20_%20_%20%0A%20%20%20%20%20%20%20%20%7C%20%7C%20%7C%0A%20%20%20_%20%20%20%20%7CA%7C%20%7C%0A%20%20%7C%20%7C_%20_%7CB%7C%20%7C%0A%20_%7C%20%7CB%7C%20%7CC%7C%20%7C%0A%7C%20%7CB%7Cc%7CB%7CD%7C%20%7C%0A%7CC%7C%20%7C%20%7Cb%7C%20%7CD%7C%0A%7C_%7C_%7C_%7C_%7C_%7C_%7C). The input should be padded with spaces to form a rectangle.
[Answer]
# Python 2 - 399 bytes
Expects the input to not have a trailing newline.
```
import sys;a=str.strip;L=list(sys.stdin);b=len(L[-1])/2;s=['']*b
for l in L:
    i=0
    for c in l[1:-1:2]:s[i]+=c;i+=1
s=sorted([a(a(x),'_')for x in s],key=a);y=map(len,s);m=[y[0]]+[max(y[i],y[i+1])for i in range(b-1)]
for i in range(max(y)+1):
    h=max(y)-i;l='';j=0
    for x in s:l+='|'if h<m[j]else' ';l+='_' if h==len(x)else' 'if h>len(x)else x[-h-1];j+=1
    print l+('|'if h<y[-1]else' ')
print'|_'*b+'|'
```
[Answer]
# CJam, ~~75~~ ~~66~~ 65 bytes
```
qN/z(;2%{_{" _"#W=}#>}$es:P;_W>+{_'_#_Pe<)S*2$,'|*.e<@@:P;}%);zN*
```
This expects input padded with spaces to form a rectangle.
[Try it online](http://cjam.aditsu.net/#code=qN%2Fz(%3B2%25%7B_%7B%22%20_%22%23W%3D%7D%23%3E%7D%24es%3AP%3B_W%3E%2B%7B_'_%23_Pe%3C)S*2%24%2C'%7C*.e%3C%40%40%3AP%3B%7D%25)%3BzN*&input=%20_%20%20%20%20%20%0A%7C%20%7C%20%20_%20%0A%7CF%7C%20%7C%20%7C%0A%7Co%7C_%7CP%7C%0A%7Co%7CB%7CP%7C%0A%7C%20%7Ca%7CC%7C%0A%7C%20%7Cr%7CG%7C%0A%7C_%7C_%7C_%7C)
Thanks to @Sp3000 and @Dennis for suggestions on string trimming on chat, as well as clueing me in that the `$` operator can take a block as an argument.
I'm still not entirely happy with the second loop. But after trying a few other options without better success, I'm getting tired.
Explanation:
```
qN/     Read input and split at newlines.
z       Transpose to turn columns into lines.
(;      Drop first line...
2%      ... and every second line after that, to keep only lines with titles.
{       Start block that maps lines for sort.
  _       Copy.
  {       Start block for matching first title letter.
    " _"#   Search for character in " _".
    W=      True if not found.
  }#      End match block. This gets position of first character not in " _".
  >       Trim leading spaces and '_.
}$      End of sort block. Lines are now sorted alphabetically by title.
es:P;   Store large number in P. P holds previous position of '_ in following loop.
_W>+    Repeat last title line, so that final separator line is generated.
{       Loop over title lines.
  _'_#    Find position of '_.
  _       Copy position. Will store it in P after the minimum has been determined.
  P       Get position of '_ in previous line.
  e<)     Take the smaller of the two '_ positions, and decrement.
  S*      Generate leading spaces from the count.
  2$,     Get length of title line.
  '|*     Generate full line length sequence of '|.
  .e<     Overlap spaces with '| to give the final separator.
  @@      Get '_ position to top, and stack in order for next loop iteration.
  :P;     Store '_ position in P.
}%      End of loop over lines.
);      Remove last line, which was a repeat.
z       Transpose to turn lines into columns again.
N*      Join with newline characters.
```
[Answer]
# Scala ~~359~~ 341 bytes
expects all lines to be of the same length (i.e. padded with spaces)
```
(s:String)=>{def f(s:String)=(" "/:s)((r,c)=>if(r.last=='|'||c=='_')r+"|"else r+" ").init;val h=s.lines.toSeq.transpose.collect{case s if s.exists(_.isLetter)=>s.mkString}.sortBy(_.filter(!_.isWhitespace));((Seq(f(h(0)))/:h.sliding(2))((s,l)=>s:+l(0):+f(l.minBy(_.indexOf('_')))):+h.last:+f(h.last)).transpose.map(_.mkString).mkString("\n")}
```
ungolfed & commented:
```
//anonymous method that takes the books ascii-art string
(s: String) => {
  //method to convert the middle to a border
  def f(s: String) =
    //fold (starting from non empty string since we use `.last`)
    (" "/:s)((r,c) =>
      if(r.last=='|'||c=='_')r+"|"
      else r+" "
    ).init.tail
  
  //h is a sequence of strings of the middle of the books
  val h =
    //transpose lines of input string, and take only the lines the contains letters (middle of the books)
    s.lines.toSeq.transpose.collect{
      case s if s.exists(_.isLetter) =>
        s.mkString
    }.sortBy(_.filter(!_.isWhitespace)) //sort the books by title (actually by "_$title" since we filter out just whitspaces)
  //fold over pairs of books and add the last manually
  (
    (Seq(f(h(0)))/:h.sliding(2))((s,l) =>
      s :+ l(0) :+ f(l.minBy(_.indexOf('_'))) //convert higher book to border and append to folded accumulator
    ) :+ h.last :+ f(h.last) //add last book manually
  ).transpose.map(_.mkString).mkString("\n") //transpose back and construct the output string
}
```
]