Hi @Peter Kahrel, thank you for this, that is a very efficient approach and helped me a lot.
With your help I have updated my script. It will not be as fast as yours because it is careful about which paragraphs to move (eg. it won't damage non-question paragraphs in the same story if they occur before or after the questions — see screen shot below), but it will be faster than my first attempt. On my computer here, using your demo document but with 1000 questions, and the "safe" paragraphs before and after, it completed in 15 seconds.
@uniq1, if you don't mind trying this, I would like to know if it is now fast enough for real use in your large document. I have an idea for a further optimization if it is still too slow.
- Mark

/**
* Sort 'questions' by 'key number', where
* questions are paragraph groupings delimited
* by the paragraph style "question". The key
* number is derived from the paragraph with
* style "key" using a regexp.
*
* Usage: select a text frame or put cursor in
* text of a story and run script.
*
* NOTE: cannot currently sort multiple stories.
*
* @author m1b - with much help from Peter Kahrel
* @discussion https://community.adobe.com/t5/indesign-discussions/i-wanna-order-my-questions-and-answers-sort-to-the-key-paragraph-style-from-smallest-to-largest/m-p/14320542
*/
var settings = {
doc: app.activeDocument,
// match the key number
keyGrep: /^Key\s*(\d+)/,
// key paragraph contains the key number
keyParagraphsStyleName: 'key',
// answer is the last paragraph in the set
answerParagraphStyleName: 'answer',
// the question paragraph style
questionParagraphStyle: (
app.activeDocument.paragraphStyleGroups.itemByName('quiz')
.paragraphStyles.itemByName('question')
),
};
function main() {
var target = settings.doc.selection[0] || settings.doc;
if ('function' !== typeof target.findGrep)
return alert('Please select a text object, or nothing, and try again.')
if (!settings.questionParagraphStyle.isValid)
return alert('Error: no valid `key` paragraph style found.');
if ('TextFrame' === target.constructor.name)
target = target.parentStory;
else if (
target.hasOwnProperty('parent')
&& 'Story' === target.parent.constructor.name
)
target = target.parent;
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
app.findGrepPreferences.appliedParagraphStyle = settings.questionParagraphStyle;
// this will be a collection of texts
var found = target.findGrep();
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
var questions = [],
story,
question,
styles,
start,
end,
match;
foundLoop:
for (var i = 0; i < found.length; i++) {
question = {};
questions.push(question);
if (undefined != story) {
if (found[i].parent !== story)
return alert('Abort: There are muliple stories to sort. Please do them one at a time.');
}
else {
story = found[i].parent;
if (story.characters[-1].contents != '\r')
// makes things easier if we have a CR here
story.insertionPoints[-1].contents = '\r';
}
start = found[i].index;
end = i === found.length - 1 ? story.characters.length - 1 : found[i + 1].index - 1;
question.characters = story.characters.itemByRange(start, end);
// use the styles to understand the question set
styles = question.characters.paragraphs.everyItem().appliedParagraphStyle;
for (var j = 0; j < styles.length; j++) {
if (
settings.keyParagraphsStyleName === styles[j].name
&& (match = question.characters.paragraphs[j].contents[0].match(settings.keyGrep))
&& 2 === match.length
)
// add the key number
question.keyNumber = Number(match[1]);
else if (
settings.answerParagraphStyleName == styles[j].name
&& end !== question.characters.paragraphs[j].characters[-1].index[0]
) {
// double-check the question set ends with the answer, and not some other text
end = question.characters.paragraphs[j].characters[-1].index[0];
question.characters = story.characters.itemByRange(start, end);
}
}
}
if (0 === questions.length)
return alert('No questions found.');
// we'll use these to remove the original questions
start = questions[0].characters.characters[0].index[0];
end = questions[questions.length - 1].characters.characters[-1].index[0];
// sort the questions by key number
questions.sort(function (a, b) { return a.keyNumber - b.keyNumber });
// create temporary frame
var tempFrame = app.documents[0].textFrames.add();
for (i = 0; i < questions.length; i++)
questions[i].characters.duplicate(LocationOptions.AT_END, tempFrame.parentStory);
// remove the original questions
story.characters.itemByRange(start, end).remove();
// clean up
tempFrame.parentStory.move(LocationOptions.AFTER, story.insertionPoints[start]);
tempFrame.remove();
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Sort Answers');
Enter your E-mail address. We'll send you an e-mail with instructions to reset your password.
