Converting an inner loop to a map makes no sense if you're trying to deal with every cell in every row in a CSV, and other such scenarios. I've talked about nested loops before; there are times when they are necessary, such as here. Replacing a nested loop with a map will not improve performance in this case. Also, replacing nested loops with maps will rarely ever solve heap limit exceptions; maps are best used to solve CPU timeouts from excessive unnecessary processing.
In order to free up heap space to avoid heap limit exceptions, you need to free memory as early as possible. You can do this by avoiding temporary variables:
List<List<String>> rowsIn = CsvReader.read([
SELECT VersionData, VersionNumber, PathOnClient
FROM ContentVersion
WHERE ContentDocumentId = :contentDocumentId
AND IsLatest = TRUE
LIMIT 1
].VersionData.toString());
This avoids leaving a copy of the string in memory as both a string and blob, reducing memory usage by up to 50%. Of course, it also depends on how your CsvReader is written. Most CSV Readers assume unlimited memory, but if your csv reader is "destructive" (i.e. it actually removes parsed content from the source string as it parses), you can get the actual memory usage down significantly.