Consolidate Individual Pages and Page Ranges

You’re right! Thanks, nutilius. I thought I’d tried it without braces, but clearly I hadn’t. I think the braces make the code look clearer in a one-liner, but that’s a separate issue. :slightly_smiling_face:

OK - I know that this is “private investigation” :wink:, but here is my proposal (in Python):

#!/usr/bin/env python3

s = '10, 23, 15-16, 1, 38-40, 4-5, 39-41, 44, 51' # , 8-19'
print(s)

# Find max page 
m = int(max(s.replace('-', ', ').replace(', ', ',').split(','), key=int))

# Page map - present pages as '*'
pages = list((m + 1) * " ")

for v in s.replace(' ', '').split(','):
    try:
		# Fill spanned area
        (b, e) = v.split('-')
        for i in range(int(b), int(e)+1): pages[i] = '*'
    except:
        # Sorry there is only one
        pages[int(v)] = '*'
        
# Remove 0-based index artefact and convert to string         
pages = "".join(pages[1:]) + " "

# Result accumulated in string to cutoff last comma
result = ""

p = pages.find('*', 0)
q = pages[p:].find(' ')
while p >= 0:
    #print(f"==>{p=} {q=}")
    if q == 1:
        result += f"{p+1},"
    else:
        result += f"{p+1}-{p+q},"    
    p = pages.find('*', p+q)
    q = pages[p:].find(' ')

# Cut-off last comma     
print(result[:-1])

I’m sorry but the last solution in bash doesn’t work with unsorted or overlapped sequences :frowning:, like for example:

1 2 3 4 6-8 8 10 12 13 2 37-39 14 17 18-20 21-22 23 35-39 99 100 101

Hi nutilius.

If you mean the script doesn’t integrate duplicates and out-of-order numbers with the others as a single set of numbers and ranges, it’s not meant to. For reasons of his own, peavine wanted the page order and duplicates to be preserved. (See post 8 above.)

Mhm my mistake, I didn’t read carefully. So I will save my solution for other situations, maybe somewhere will be usable.

Just for learning purposes, I edited Nigel’s shell script to work with the Zsh shell. First, I substituted -A for -a as a read command option. Second, I made those changes necessary to work with 1- instead of 0-based arrays. I did not edit the test equality operators, because the script seemed to work without this change, and I wasn’t sure if the test should be for string or numeric equality (both seem to work).

#!/bin/zsh

read -A input_array <<< "1 2 3 4 6-8 8 10 12 13 2 14 17 18-20 21-22 23"
input_count=${#input_array[@]}
item=${input_array[1]} # array is 1-based
range_start=${item%%-*}
range_end=${item#*-}

for (( i=2; $i <= $input_count; i++ )); do
  item=${input_array[i]}
  pn1=${item%%-*}
  pn2=${item#*-}
  if [[ $((range_end + 1 )) == $pn1 ]]; then # tests for string equality
    range_end=$pn2
  else
    new_item=$range_start
    if [[ $range_end != $range_start ]]; then # tests for string inequality
      new_item+="-$range_end"
    fi
    output+="$new_item "
    range_start=$pn1
    range_end=$pn2
  fi
done

new_item=$range_start
if [[ $range_end != $range_start ]]; then
  new_item+="-$range_end"
fi
output+="$new_item"

printf "$output"

# For testing if swiftDialog app installed
# dialog --title "Script Test" --titlefont "size=15" --message "$output" --messagefont "size=13" --messageposition bottom --width 300 --height 150 --hideicon

I also tested without issue in a shortcut:

Zsh Shell Script.shortcut (21.9 KB)

I ran timing tests and there were no differences between the Bash and Zsh scripts.