How to sort comments in the “edit-comments.php” table based on a comment meta field?

Is it possible to use a different hook in the 4th step?

Yes, see parse_comment_query which is also an action hook, but it fires before pre_get_comments.

Is it possible to somehow alter the hearts_orderby() function in the
4th step to get the desired outcome?

Yes.

But first off, you should know that unlike the WP_Query class, WP_Comment_Query does not (currently) have a set() method, so despite calling $comments_query->set() did not cause any error, the meta_key and orderby args were never actually set.

( WP_Comment_Query has a magic __call() method, hence calling non-existent methods such as the set() would simply return false without doing anything else )

Now to make your function works, just set the meta query via direct modification on the $comments_query->query_vars array like so:

if( 'by_hearts' == $orderby ) {
    $comments_query->query_vars['meta_key'] = 'hearts';
    $comments_query->query_vars['orderby'] = 'meta_value_num';
}

If, however, you want to include comments without the hearts meta, then you can instead use the meta_query arg:

$meta_query = isset( $comments_query->query_vars['meta_query'] ) ?
    (array) $comments_query->query_vars['meta_query'] : array();

$meta_query[] = array(
    'relation'  => 'OR',

    // Include comments having the meta.
    // Match the array key with the $orderby value.
    'by_hearts' => array(
        'key'  => 'hearts',
        'type' => 'NUMERIC',
    ),

    // Include comments without the meta.
    'no_hearts' => array(
        'key'     => 'hearts',
        'compare' => 'NOT EXISTS',
    ),
);

$comments_query->query_vars['meta_query'] = $meta_query;