Optimal way to display a player's health with hearths?

I wanted to display a simplified version of the classical zelda’s hearts: the player’s hp are an integer value (between 1 and 3) and i just want to display that number of hearts in a corner of the screen.

I’m getting started with UI and my first idea was to have three separate sprites and changing them individually via script whenever the player’s hp is updated. Is there a more elegant way to do this?

I’m not sure what exactly you mean by “classical”. Different Zelda games had different heart mechanics. You probably mean that you can have a full heart, half a heart or an empty heart per container? Anyways even when you want the BOTW style where a heart can be split into a quater heart, the process is almost always the same. Of course you simply have a sprite for each state of a heart. So in the case of half-hearts that’s 3, in case of quater hearts that’s 5. You simply have an hp value which is distributed across your heart containers. For any given hp value you get the number of full hearts by doing

int fullHeartCount = hp / hpPerHeart;

where hp and hpPerHeart are integer values. “hpPerHeart” is “2” for the half-heart split and “4” for the quater heart split. To figure out the state of the heart at the the end you just do

int spriteIndex = hp % hpPerHeart;

This gives you a value of “0” for an empty heart and increasing values for partial hearts. In case of half hearts the only other value is 1 which is half a heart. In case of quater hearts you get values 0,1,2,3 which respectively belongs to: “empty heart”, “quater”, “half”, “three quater”. You can easily setup an array of sprites with that increasing order.

So whenever you want to update the hp you simply calculate the two values from above and iterate through your all your heart containers from lowest to highest and do

int fullHeartCount = hp / hpPerHeart;
int spriteIndex = hp % hpPerHeart;
for(int i = 0; i < heartContainerCount; i++)
{
    if (i < fullHeartCount)
        container[i].sprite = spriteArray[spriteArray.Length-1];
    else if (i == fullHeartCount)
        container[i].sprite = spriteArray[spriteIndex];
    else
        container[i].sprite = spriteArray[0];
}

Keep in mind that the sprites in the spriteArray have to be in increasing order where the empty heart is at index 0 and the full heart at the highest index. This code will work with any subdivision.
If you also want to support “overhealing” (i.e. yellow hearts) you would simply use an additional sprite array with the same layout just with the alternative graphics. All you have to do is at the beginning of the for loop, pick one of the two arrays based on if the i value is below or above the current container count. Of course your for loop would iterate through all containers possible.
Note that this is just about displaying the hearts. It does not account any animation. However this could theoretically be done in the heart container itself.

edit
I hade some time and made a “programmer art” health bar example that somewhat represents the behaviour of BOTW. You can grab the example package from here. Keep in mind that this is just an example. But in theory you could simply replace the two graphics for the red and yellow “hearts” with your own and it should work just fine. Please keep in mind that this scripts are only meant for displaying the health, not for managing it. You can use the methods SetHealth() and SetContainerCount() to update the health bar. It is build in a modular way so you can easily extend or rearrange the individual hearts any way you like.

Note that the current max health (without overhealing) is determined by the container count multiplied by the health per container, which is 4 in this example. Each container is a seperate object. You just have to give each container the proper index value

To keep the overhealing amount when you add / remove containers you should also add / remove the same amount of HP