diff --git a/devTools/types/FC/global.d.ts b/devTools/types/FC/global.d.ts
index 3a562c171ea5f29b7b63c63ae370baf7b01e2cd0..ba8cd416540d0ee07d7618ca6dccdf6480282d10 100644
--- a/devTools/types/FC/global.d.ts
+++ b/devTools/types/FC/global.d.ts
@@ -1,7 +1,6 @@
 declare global {
 	// extensions
 	interface Array<T> {
-		toStringExt(delimiter?: string, lastDelimiter?: string): string;
 		includes(needle: any): boolean; // because we use silly unions with 0
 	}
 
diff --git a/js/utils.js b/js/utils.js
index 3ef5bd4eee47a1e3dc98cb2a3d5c84c062e176ce..e2b3220d82876515ac235156d44722652e450b20 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -185,29 +185,6 @@ function addA(word) {
 	return `a ${word}`;
 }
 
-Object.defineProperty(Array.prototype, 'toStringExt', {
-	/**
-	 * @param {string} delimiter
-	 * @param {string} lastDelimiter
-	 * @returns {string}
-	 */
-	value(delimiter = ', ', lastDelimiter = ' and ') {
-		if (this == null) {
-			throw new TypeError('Array.prototype.toStringExt called on null or undefined');
-		}
-		if (this.length === 0) {
-			return "none";
-		}
-		if (this.length === 1) {
-			return this[0].toString();
-		}
-		const last = this.pop();
-		let r = this.join(delimiter);
-		this.push(last); // don't leave the array modified
-		return `${r}${lastDelimiter}${last}`;
-	}
-});
-
 /**
  * @param {number} i
  * @returns {string}
diff --git a/src/arcologyBuilding/manufacturing.js b/src/arcologyBuilding/manufacturing.js
index 81f321bcdca9b02f71930ac5b42882a22261dd2d..188a9b7f5cd496baf32a8a3ca6f0083cc4db5e66 100644
--- a/src/arcologyBuilding/manufacturing.js
+++ b/src/arcologyBuilding/manufacturing.js
@@ -160,7 +160,7 @@ App.Arcology.Cell.Manufacturing = class extends App.Arcology.Cell.BaseCell {
 					list.push(numberWithPluralOne(V.fuckdolls, "standard Fuckdoll"));
 				}
 
-				r += `${list.toStringExt()}, `;
+				r += `${toSentence(list)}, `;
 
 				if (V.menials + V.menialBioreactors + V.fuckdolls > 500) {
 					r += "partially ";
diff --git a/src/endWeek/economics/arcmgmt.js b/src/endWeek/economics/arcmgmt.js
index d9c550e87456ec8dcae279673128ff961bd6d53e..bc84b9d26d9c1dbd8151e0c1cf62b3e7bc53c16c 100644
--- a/src/endWeek/economics/arcmgmt.js
+++ b/src/endWeek/economics/arcmgmt.js
@@ -558,9 +558,9 @@ App.EndWeek.arcManagement = function() {
 		if (_desc.length > 0) {
 			r.push(`Your arcology's economy benefits from close social alignment with`);
 			if (_descNeg.length > 0) {
-				r.push(`${arrayToSentence(_desc)}, but`);
+				r.push(`${toSentence(_desc)}, but`);
 			} else {
-				r.push(`${arrayToSentence(_desc)}.`);
+				r.push(`${toSentence(_desc)}.`);
 			}
 			_AWeekGrowth += _desc.length;
 		}
@@ -568,7 +568,7 @@ App.EndWeek.arcManagement = function() {
 			if (_desc.length === 0) {
 				r.push(`Your arcology's economy is`);
 			}
-			r.push(`hindered by social conflicts with ${arrayToSentence(_descNeg)}.`);
+			r.push(`hindered by social conflicts with ${toSentence(_descNeg)}.`);
 			_AWeekGrowth -= _descNeg.length;
 		}
 		if (V.policies.alwaysSubsidizeGrowth === 1) {
@@ -1852,7 +1852,7 @@ App.EndWeek.arcManagement = function() {
 			z.push(numberWithPluralOne(_deathsTC, "millionaire"));
 		}
 		if (_deathsLC > 0 || _deathsMC > 0 || _deathsUC > 0 || _deathsTC > 0) {
-			r.push(App.UI.DOM.makeElement("span", `${capFirstChar(arrayToSentence(z))} passed away due to natural causes.`, "red"));
+			r.push(App.UI.DOM.makeElement("span", `${capFirstChar(toSentence(z))} passed away due to natural causes.`, "red"));
 		}
 
 		/* Slave expiration*/
diff --git a/src/endWeek/economics/neighborsDevelopment.js b/src/endWeek/economics/neighborsDevelopment.js
index ddade6c6d499fd290c293992eb342173b3e0cff8..ccbc6650db4754765d9f55e76ef7c060a9d5ac00 100644
--- a/src/endWeek/economics/neighborsDevelopment.js
+++ b/src/endWeek/economics/neighborsDevelopment.js
@@ -201,7 +201,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				}
 				const decayedFSes = FutureSocieties.decay(i).map((fs) => FutureSocieties.displayName(fs));
 				if (decayedFSes.length > 2) {
-					r.push(`Its citizens take the opportunity to make radical social changes, <span class="cyan">purging the ${arrayToSentence(decayedFSes)}</span> favored by the old government.`);
+					r.push(`Its citizens take the opportunity to make radical social changes, <span class="cyan">purging the ${toSentence(decayedFSes)}</span> favored by the old government.`);
 				} else if (decayedFSes.length === 2) {
 					r.push(`Its citizens take the opportunity to make social changes, <span class="cyan">discarding the ${decayedFSes[0]} and ${decayedFSes[1]}</span> favored by the old government.`);
 				} else if (decayedFSes.length === 1) {
@@ -1475,11 +1475,11 @@ App.EndWeek.neighborsDevelopment = function() {
 					if (helping.length === 0 && attacking.length === 0) {
 						r.push(`<span class="bold">${arc2.name}</span> attempts to influence it, but has no significant impacts.`);
 					} else if (helping.length === 0) {
-						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, attacking its ${arrayToSentence(attacking)}.`);
+						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, attacking its ${toSentence(attacking)}.`);
 					} else if (attacking.length === 0) {
-						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, helping to advance its ${arrayToSentence(helping)}.`);
+						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, helping to advance its ${toSentence(helping)}.`);
 					} else {
-						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, helping to advance its ${arrayToSentence(helping)}, while attacking its ${arrayToSentence(attacking)}.`);
+						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, helping to advance its ${toSentence(helping)}, while attacking its ${toSentence(attacking)}.`);
 					}
 
 					if (appliedInfluenceBonus > 0) {
diff --git a/src/endWeek/reports/clinicReport.js b/src/endWeek/reports/clinicReport.js
index 139a5bef87b3fda6aaf7387e38bea89525b4c62e..7866fd66217c390160c26147055a5b242e645b20 100644
--- a/src/endWeek/reports/clinicReport.js
+++ b/src/endWeek/reports/clinicReport.js
@@ -409,7 +409,7 @@ App.EndWeek.clinicReport = function() {
 			const patientContent = App.UI.DOM.appendNewElement("div", slaveEntry, '', "indent");
 			$(patientContent).append(`${He} ${App.SlaveAssignment.rest(slave)} `);
 			if (remainReasons.length > 0) {
-				$(patientContent).append(`${He} stays in the clinic ${arrayToSentence(remainReasons)}.`);
+				$(patientContent).append(`${He} stays in the clinic ${toSentence(remainReasons)}.`);
 			}
 			slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
diff --git a/src/endWeek/saLongTermPhysicalEffects.js b/src/endWeek/saLongTermPhysicalEffects.js
index 2bbef115bf6c8360c2dcb4388e8ecda10dd9ff64..3fa1aeb9b5cb87fc848665a8de5c5325f72200dd 100644
--- a/src/endWeek/saLongTermPhysicalEffects.js
+++ b/src/endWeek/saLongTermPhysicalEffects.js
@@ -1529,7 +1529,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 			if (mobility) {
 				r.push(`${His}`);
 				if (hindrances.length > 0) {
-					r.push(arrayToSentence(hindrances));
+					r.push(toSentence(hindrances));
 					if (isAmputee(slave)) {
 						if (hindrances.length > 1) {
 							r.push(`but ultimately ${his} limblessness,`);
@@ -1575,7 +1575,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 			} else {
 				r.push(`${He} is immobilized by ${his}`);
 				if (hindrances.length > 0) {
-					r.push(arrayToSentence(hindrances));
+					r.push(toSentence(hindrances));
 					if (isAmputee(slave)) {
 						if (hindrances.length > 1) {
 							r.push(`but ultimately ${his} limblessness,`);
diff --git a/src/endWeek/saPleaseYou.js b/src/endWeek/saPleaseYou.js
index 60daadf9a0820905d7450fc4fab5b66a78654118..dffa7ce831760b6f406bff3916fb42d309384fa6 100644
--- a/src/endWeek/saPleaseYou.js
+++ b/src/endWeek/saPleaseYou.js
@@ -1644,7 +1644,7 @@ App.SlaveAssignment.pleaseYou = (function() {
 
 		children = V.slaves.filter(s => (s.father === slave.ID || s.mother === slave.ID) && isFucktoy(s));
 		if (children.length > 1) {
-			r.push(`Since you are also keeping ${his} daughters, ${arrayToSentence(children.map(s => s.slaveName))}, as sexual servants, you often enjoy them all at once, which leaves quite a public impression.`);
+			r.push(`Since you are also keeping ${his} daughters, ${toSentence(children.map(s => s.slaveName))}, as sexual servants, you often enjoy them all at once, which leaves quite a public impression.`);
 		} else if (children.length > 0) {
 			const childPronouns = getPronouns(children[0]);
 			r.push(`Since you are also keeping ${his} ${childPronouns.daughter} as a sexual servant, you often use them together, which leaves quite a public impression.`);
@@ -1652,7 +1652,7 @@ App.SlaveAssignment.pleaseYou = (function() {
 
 		children = V.slaves.filter((s) => (areSisters(slave, s) > 0) && isFucktoy(s));
 		if (children.length > 1) {
-			r.push(`Since you are also keeping ${his} sisters, ${arrayToSentence(children.map(s => s.slaveName))}, as sexual servants, you often enjoy them all at once, which leaves quite a public impression.`);
+			r.push(`Since you are also keeping ${his} sisters, ${toSentence(children.map(s => s.slaveName))}, as sexual servants, you often enjoy them all at once, which leaves quite a public impression.`);
 		} else if (children.length > 0) {
 			const childPronouns = getPronouns(children[0]);
 			r.push(`Since you are also keeping ${his} ${childPronouns.sister} as a sexual servant, you often use them together, which leaves quite a public impression.`);
diff --git a/src/endWeek/saRelationships.js b/src/endWeek/saRelationships.js
index a2cdf131945dbedafa96eaf52a6c21f4a31f37ce..0060849530424be9fad73e5147b6ee311f7f30cd 100644
--- a/src/endWeek/saRelationships.js
+++ b/src/endWeek/saRelationships.js
@@ -1200,7 +1200,7 @@ App.SlaveAssignment.relationships = (function() {
 			let groups = [];
 			for (const [type, people] of map) {
 				if (people.length > 1) {
-					groups.push(`${his} ${type}s ${arrayToSentence(people.map(s => s.slaveName))}`);
+					groups.push(`${his} ${type}s ${toSentence(people.map(s => s.slaveName))}`);
 				} else {
 					groups.push(`${his} ${type} ${people[0].slaveName}`);
 				}
@@ -1258,7 +1258,7 @@ App.SlaveAssignment.relationships = (function() {
 					}
 				}
 				if (worriedAboutChildren.length > 1) {
-					r.push(`${slave.slaveName} is <span class="trust dec">agonizingly aware</span> that ${his} children ${arrayToSentence(worriedAboutChildren.map(s => s.slaveName))} are also your slaves and might suffer if any of them angers you, and <span class="devotion inc">does ${his} best</span> to protect them.`);
+					r.push(`${slave.slaveName} is <span class="trust dec">agonizingly aware</span> that ${his} children ${toSentence(worriedAboutChildren.map(s => s.slaveName))} are also your slaves and might suffer if any of them angers you, and <span class="devotion inc">does ${his} best</span> to protect them.`);
 				} else if (worriedAboutChildren.length > 0) {
 					const {him2} = getPronouns(worriedAboutChildren[0]).appendSuffix("2");
 					r.push(`${slave.slaveName} is <span class="trust dec">agonizingly aware</span> that ${his} child ${worriedAboutChildren[0].slaveName} is also your slave and might suffer if either of them angers you, and <span class="devotion inc">does ${his} best</span> to protect ${him2}.`);
@@ -1271,7 +1271,7 @@ App.SlaveAssignment.relationships = (function() {
 					r.push(`${slave.slaveName} is <span class="trust dec">painfully conscious</span> that ${his} ${relativeTerm(slave, singleRelative)} ${singleRelative.slaveName} is also your slave and might suffer if either of them displeases you, and <span class="devotion inc">tries to obey</span> as best ${he} can.`);
 				} else if (worriedAboutRelatives.size > 0) {
 					const groups = relativeMapToGroupArray(worriedAboutRelatives);
-					r.push(`${slave.slaveName} is <span class="trust dec">painfully conscious</span> that ${arrayToSentence(groups)} are also your slaves and might suffer if any of them displeases you, and <span class="devotion inc">tries to obey</span> as best ${he} can.`);
+					r.push(`${slave.slaveName} is <span class="trust dec">painfully conscious</span> that ${toSentence(groups)} are also your slaves and might suffer if any of them displeases you, and <span class="devotion inc">tries to obey</span> as best ${he} can.`);
 				}
 				if (relativeMapTotalSize(worriedAboutRelatives) > overwhelmed) {
 					r.push(`${He} has so many relatives to worry about that ${he} is overwhelmed with fear and <span class="trust inc">forced to trust you.</span>`);
@@ -1304,7 +1304,7 @@ App.SlaveAssignment.relationships = (function() {
 					r.push(`${slave.slaveName} knows that ${his} ${relativeTerm(slave, singleRelative)} ${singleRelative.slaveName} loves being your sex slave, and is <span class="devotion inc">happy</span> for ${him2}.`);
 				} else if (devotedRelatives.size > 0) {
 					const groups = relativeMapToGroupArray(devotedRelatives);
-					r.push(`${slave.slaveName} knows that ${arrayToSentence(groups)} all love being your sex slaves, and is <span class="devotion inc">happy</span> for them.`);
+					r.push(`${slave.slaveName} knows that ${toSentence(groups)} all love being your sex slaves, and is <span class="devotion inc">happy</span> for them.`);
 				}
 				if (relativeMapTotalSize(devotedRelatives) > overwhelmed) {
 					r.push(`${He} has so many relatives that love being your slaves that ${he} is sometimes overwhelmed with joy and <span class="devotion dec">neglects ${his} duties.</span>`);
@@ -1315,7 +1315,7 @@ App.SlaveAssignment.relationships = (function() {
 					r.push(`${slave.slaveName} knows that ${his} ${relativeTerm(slave, singleRelative)} ${singleRelative.slaveName} is an obedient sex slave, and hopes ${he2}'ll avoid punishment.`);
 				} else if (obedientRelatives.size > 0) {
 					const groups = relativeMapToGroupArray(obedientRelatives);
-					r.push(`${slave.slaveName} knows that ${arrayToSentence(groups)} are obedient sex slaves, and hopes they'll avoid punishment.`);
+					r.push(`${slave.slaveName} knows that ${toSentence(groups)} are obedient sex slaves, and hopes they'll avoid punishment.`);
 				}
 				if (relativeMapTotalSize(obedientRelatives) > overwhelmed) {
 					r.push(`${He} has so many obedient relatives that ${he} sometimes forgets about some of them.`);
@@ -1326,7 +1326,7 @@ App.SlaveAssignment.relationships = (function() {
 					r.push(`${slave.slaveName} knows that ${his} ${relativeTerm(slave, singleRelative)} ${singleRelative.slaveName} hates being a sex slave, and is <span class="trust dec">afraid</span> for ${him2}.`);
 				} else if (hatefulRelatives.size > 0) {
 					const groups = relativeMapToGroupArray(hatefulRelatives);
-					r.push(`${slave.slaveName} knows that ${arrayToSentence(groups)} all hate being sex slaves, and is <span class="trust dec">afraid</span> for them.`);
+					r.push(`${slave.slaveName} knows that ${toSentence(groups)} all hate being sex slaves, and is <span class="trust dec">afraid</span> for them.`);
 				}
 				if (relativeMapTotalSize(hatefulRelatives) > overwhelmed) {
 					r.push(`${He} has so many relatives that hate being your sex slaves that ${he} is overwhelmed with fear and <span class="trust inc">just has to trust you to take care of them.</span>`);
diff --git a/src/endWeek/saServeThePublic.js b/src/endWeek/saServeThePublic.js
index 05017788ef200e4ea2f8bd843d46213fe7c0f7e3..506aecf7736b5c597685b0be60b47b3b5a51a2a3 100644
--- a/src/endWeek/saServeThePublic.js
+++ b/src/endWeek/saServeThePublic.js
@@ -588,7 +588,7 @@ App.SlaveAssignment.serveThePublic = (function() {
 		if (totalRelatives(slave) > 0) {
 			let children = V.slaves.filter((s) => areRelated(slave, s) && (s.assignment === slave.assignment));
 			if (children.length > 1) {
-				r += ` Since ${his} relatives, ${arrayToSentence(children.map(s => s.slaveName))}, are public sluts too, ${he} earns extra attention.`;
+				r += ` Since ${his} relatives, ${toSentence(children.map(s => s.slaveName))}, are public sluts too, ${he} earns extra attention.`;
 			} else if (children.length > 0) {
 				r += ` Since ${his} relative, ${children[0].slaveName}, is a public slut too, ${he} earns extra attention.`;
 			}
diff --git a/src/endWeek/saSmartPiercingEffects.js b/src/endWeek/saSmartPiercingEffects.js
index 56bdc815258f98808601d286cc9bf1c209807011..a53baf79f9dfea7739b71f3ba5c5b6e4a2b1aaa5 100644
--- a/src/endWeek/saSmartPiercingEffects.js
+++ b/src/endWeek/saSmartPiercingEffects.js
@@ -497,6 +497,6 @@ App.SlaveAssignment.saSmartPiercingEffects = function(slave) {
 			effects.push(`<span class="libido dec">reduces</span> ${his} overcharged libido`);
 			slave.energy -= 3;
 		}
-		return effects.length > 0 ? intro + arrayToSentence(effects) + "." : "";
+		return effects.length > 0 ? intro + toSentence(effects) + "." : "";
 	}
 };
diff --git a/src/endWeek/saSocialEffects.js b/src/endWeek/saSocialEffects.js
index e7aa1ba860827c2238a17b8a403b09cf8097d7e7..421f7141158152f85e580ee85fa33ef514ddb2b3 100644
--- a/src/endWeek/saSocialEffects.js
+++ b/src/endWeek/saSocialEffects.js
@@ -651,7 +651,7 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 			}
 			if (addons.length > 0) {
 				t.push(new SocialEffect("Transformation Fetishist", Math.min(addons.length, 2), `Transhuman addons`,
-					`Society <span class="green">strongly approves</span> of ${his} transhuman ${arrayToSentence(addons)}.`));
+					`Society <span class="green">strongly approves</span> of ${his} transhuman ${toSentence(addons)}.`));
 				transformed += addons.length;
 			}
 			if (slave.dick > 8) {
@@ -704,7 +704,7 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 			}
 			if (unslim.length > 0) {
 				t.push(new SocialEffect("Slimness Enthusiast", -1, `Big tits/ass`,
-					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s boorishly large ${arrayToSentence(unslim)}; ${he} damages the fashion for slender slaves.`));
+					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s boorishly large ${toSentence(unslim)}; ${he} damages the fashion for slender slaves.`));
 			}
 		} else if (V.arcologies[0].FSAssetExpansionist !== "unset") {
 			let assets = 0;
diff --git a/src/endWeek/saWhore.js b/src/endWeek/saWhore.js
index e65a21af4284e37db187629e33c5dedc18fbed23..f07cdf4ab58a02949a37c574e4ced838bc1721ac 100644
--- a/src/endWeek/saWhore.js
+++ b/src/endWeek/saWhore.js
@@ -663,7 +663,7 @@ App.SlaveAssignment.whore = (function() {
 		if (totalRelatives(slave) > 0) {
 			let children = V.slaves.filter((s) => areRelated(slave, s) && (s.assignment === slave.assignment));
 			if (children.length > 1) {
-				r += ` Since ${his} relatives, ${arrayToSentence(children.map(s => s.slaveName))}, are selling themselves too, ${he} earns extra ¤ by working with them.`;
+				r += ` Since ${his} relatives, ${toSentence(children.map(s => s.slaveName))}, are selling themselves too, ${he} earns extra ¤ by working with them.`;
 			} else if (children.length > 0) {
 				const relativePronouns = getPronouns(children[0]);
 				r += ` Since ${his} relative, ${children[0].slaveName}, is selling ${relativePronouns.objectReflexive} too, ${he} earns extra ¤ by working with ${relativePronouns.object}.`;
diff --git a/src/events/reArcologyInspection.js b/src/events/reArcologyInspection.js
index 2115068222e38d4078d951c78a0604c9be85afce..0782bea652886cb5dffc0873455b7380c015e2ba 100644
--- a/src/events/reArcologyInspection.js
+++ b/src/events/reArcologyInspection.js
@@ -72,7 +72,7 @@ App.Events.REArcologyInspection = class REArcologyInspection extends App.Events.
 		if (uniqueFSes.length) {
 			t.push(`You pay special attention to the ones that are most different from what you've configured in ${home.name}, like`);
 			const diffShows = this.getShows(uniqueFSes, arcology);
-			t.push(arrayToSentence(diffShows) + ".");
+			t.push(toSentence(diffShows) + ".");
 		}
 		if (dipFSes.shared.length) {
 			if (!uniqueFSes.length) {
@@ -81,7 +81,7 @@ App.Events.REArcologyInspection = class REArcologyInspection extends App.Events.
 				t.push(`Most of the rest seem a lot like what you'd find in ${home.name}, such as`);
 			}
 			const diffShows = this.getShows(dipFSes.shared, arcology);
-			t.push(arrayToSentence(diffShows) + ".");
+			t.push(toSentence(diffShows) + ".");
 			if (!uniqueFSes.length) {
 				t.push(`${arcology.name}, while different than ${home.name}, still manages to feel a bit like home.`);
 			}
diff --git a/src/interaction/siWardrobe.js b/src/interaction/siWardrobe.js
index 80272e91028086e097350817e99e6ea18abe0cc9..3d20a4603daad631d0bec6515cf22400a4bde002 100644
--- a/src/interaction/siWardrobe.js
+++ b/src/interaction/siWardrobe.js
@@ -999,7 +999,7 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 							lovers.push(App.Data.FutureSociety.records[FS].noun);
 						}
 						if (lovers.length > 0) {
-							desc.push(`${arrayToSentence(lovers)} will ${value} this look.`); // TODO: Please move tooltip to array when that becomes possible
+							desc.push(`${toSentence(lovers)} will ${value} this look.`); // TODO: Please move tooltip to array when that becomes possible
 						}
 					}
 				}
diff --git a/src/js/birth/birth.js b/src/js/birth/birth.js
index f48b1db43f759b194d14f2a441120879d9575327..0e280b5dd9b652e8c34e69739cee0e779e26d049 100644
--- a/src/js/birth/birth.js
+++ b/src/js/birth/birth.js
@@ -466,7 +466,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				r.push(`${pregNumberName(numBeingBorn, 2)},`);
 			}
 			if (numBeingBorn > 0) {
-				r.push(`created by ${arrayToSentence(fathersReduced)}${(cSection) ? ', entered the world' : ''}.`);
+				r.push(`created by ${toSentence(fathersReduced)}${(cSection) ? ', entered the world' : ''}.`);
 			}
 			if (numStillborn > 0 && numBeingBorn > 0) {
 				r.push(`An additional ${numStillborn}`);
@@ -2114,7 +2114,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				} else {
 					r.push(`${wl} babies`);
 				}
-				r.push(`created by ${arrayToSentence(fathersReduced)} died with ${him}, too young to survive.`);
+				r.push(`created by ${toSentence(fathersReduced)} died with ${him}, too young to survive.`);
 				r.push(deadBabiesResponse(wl));
 			}
 
diff --git a/src/js/slaveListing.js b/src/js/slaveListing.js
index 050b81f13a45eb713bd52609af68075064c35287..e5c5c526cf2458e5e2660ed3c99c21aee55ce6e0 100644
--- a/src/js/slaveListing.js
+++ b/src/js/slaveListing.js
@@ -199,7 +199,7 @@ App.UI.SlaveList.render = function() {
 				}
 			}
 			if (list.length > 0) {
-				assignment.innerText += ` for ${arrayToSentence(list)}`;
+				assignment.innerText += ` for ${toSentence(list)}`;
 			} else {
 				assignment.innerText += ", preparing to check out";
 			}
@@ -218,7 +218,7 @@ App.UI.SlaveList.render = function() {
 					list.push(`improving in health`);
 				}
 				if (list.length > 0) {
-					assignment.innerText += `, ${arrayToSentence(list)}`;
+					assignment.innerText += `, ${toSentence(list)}`;
 				} else {
 					assignment.innerText += ", preparing to move out";
 				}
@@ -253,7 +253,7 @@ App.UI.SlaveList.render = function() {
 				lessons.push(`being a good ${role}`);
 			}
 			if (lessons.length > 0) {
-				assignment.innerText += `, practicing ${arrayToSentence(lessons)}`;
+				assignment.innerText += `, practicing ${toSentence(lessons)}`;
 			} else {
 				assignment.innerText += ", studying for finals";
 			}
diff --git a/src/js/utilsMisc.js b/src/js/utilsMisc.js
index 8cc03150fd2717d7ef4ab91b29d9ba9b00420418..98b77590719c035930c9f0c2d3f19f9effe431d4 100644
--- a/src/js/utilsMisc.js
+++ b/src/js/utilsMisc.js
@@ -65,12 +65,39 @@ globalThis.Categorizer = class {
 };
 
 /**
- * Converts an array of strings into a sentence parted by commas.
- * @param {Array} array ["apple", "banana", "carrot"]
- * @returns {string} "apple, banana and carrot"
+ * Converts an Iterable of strings into a sentence parted by commas.
+ * For an empty Iterable will return an empty string.
+ * @param {Iterable<string>} iterable
+ * @param {string} [delimiter=", "]
+ * @param {string} [lastDelimiter=" and "]
+ * @returns {string}
+ *
+ * @example array
+ * toSentence(["apple", "banana", "carrot"]);
+ * // returns "apple, banana and carrot"
+ *
+ * @example Set
+ * toSentence(new Set(["apple", "banana"]), ", ", " or ");
+ * // returns "apple or banana"
  */
-globalThis.arrayToSentence = function(array) {
-	return array.reduce((res, ch, i, arr) => res + (i === arr.length - 1 ? ' and ' : ', ') + ch);
+globalThis.toSentence = function(iterable, delimiter = ", ", lastDelimiter = " and ") {
+	/** @type {Array<string>} */
+	let r = [];
+	/** @type {string} */
+	let last = undefined;
+	for (const iter of iterable) {
+		if (last !== undefined) {
+			r.push(last);
+		}
+		last = iter;
+	}
+	if (r.length > 0) {
+		return r.join(delimiter) + lastDelimiter + last;
+	} else if (last !== undefined) {
+		return last;
+	} else {
+		return "";
+	}
 };
 
 App.Utils.alphabetizeIterable = function(iterable) {
diff --git a/src/js/utilsPC.js b/src/js/utilsPC.js
index 7c843d0768302cf81d48165e4b35e2c546e921fb..282c0193d2c97bc7ceeb6481e9b0a40e5510c524 100644
--- a/src/js/utilsPC.js
+++ b/src/js/utilsPC.js
@@ -524,10 +524,10 @@ globalThis.PCTitle = function() {
 	}
 
 	if (schoolsPerfected.length > 0) {
-		titles.push(`${V.PC.title === 1 ? 'Benefactor' : 'Benefactrix'} of ${schoolsPerfected.toStringExt()}`);
+		titles.push(`${V.PC.title === 1 ? 'Benefactor' : 'Benefactrix'} of ${toSentence(schoolsPerfected)}`);
 	}
 	if (schoolsPresent.length > 0) {
-		titles.push(`Supporter of ${schoolsPresent.toStringExt()}`);
+		titles.push(`Supporter of ${toSentence(schoolsPresent)}`);
 	}
 
 	if (V.TFS.schoolProsperity >= 10) {
@@ -590,7 +590,7 @@ globalThis.PCTitle = function() {
 		titles.push("owner of the arcology");
 	}
 
-	return title + titles.toStringExt();
+	return title + toSentence(titles);
 };
 
 /**
diff --git a/src/js/utilsUnits.js b/src/js/utilsUnits.js
index bb2ca3278f4b2654efcd1842b3420e60e24feec9..f6148acebff83fdde93ee95bacb5fbf7e6a6ec32 100644
--- a/src/js/utilsUnits.js
+++ b/src/js/utilsUnits.js
@@ -258,7 +258,7 @@ globalThis.years = function(weeks) {
 		array.push(`${num(weeks)} week${weeks !== 1 ? `s` : ``}`);
 	}
 
-	return array.toStringExt();
+	return toSentence(array);
 };
 /**
  * @param {number} [weeks]
diff --git a/src/neighbor/neighborInteract.js b/src/neighbor/neighborInteract.js
index 0c33267cb3be5417179b872eec9afc5437aee92d..66cf7cd42886d7adace98bda5ae916db2addc236 100644
--- a/src/neighbor/neighborInteract.js
+++ b/src/neighbor/neighborInteract.js
@@ -67,7 +67,7 @@ App.Neighbor.Interact = (function() {
 			if (desc.length === 0) {
 				App.UI.DOM.appendNewElement("p", frag, `Your arcology's culture has not developed to the point where it can meaningfully influence other arcologies.`);
 			} else if (desc.length > 2) {
-				App.UI.DOM.appendNewElement("p", frag, `Your arcology's mature culture is capable of exerting great cultural sway over other arcologies. It can readily project ${arrayToSentence(desc)}.`);
+				App.UI.DOM.appendNewElement("p", frag, `Your arcology's mature culture is capable of exerting great cultural sway over other arcologies. It can readily project ${toSentence(desc)}.`);
 			} else if (desc.length === 2) {
 				App.UI.DOM.appendNewElement("p", frag, `Your arcology's culture is capable of exerting some cultural sway over other arcologies. It can effectively project ${desc[0]} and ${desc[1]}.`);
 			} else {
diff --git a/src/npc/databases/cheatmodeDatabase.js b/src/npc/databases/cheatmodeDatabase.js
index f293c2427bb447602e49d3b20df6166798dc9fcc..daf66df30d402abcfeb10af116fd1edfed8f38cf 100644
--- a/src/npc/databases/cheatmodeDatabase.js
+++ b/src/npc/databases/cheatmodeDatabase.js
@@ -416,5 +416,5 @@ App.Intro.cheatModeSlaves = function() {
 	newSlave(cheatSlave);
 	slaveNames.push(cheatSlave.slaveName);
 
-	return `There are quite a few left; their names are ${arrayToSentence(slaveNames)}.`;
+	return `There are quite a few left; their names are ${toSentence(slaveNames)}.`;
 };
diff --git a/src/npc/descriptions/career.js b/src/npc/descriptions/career.js
index 79f96f648c907f0da2cb2c3336566be461764b84..427f43796960d1635c88fda2b65898b1d3a1d4ef 100644
--- a/src/npc/descriptions/career.js
+++ b/src/npc/descriptions/career.js
@@ -159,7 +159,7 @@ App.Desc.career = function(slave) {
 	];
 	const careersArray = careerMap.filter((o) => (slave.skill[o.prop] >= V.masteredXP)).map((o) => o.val);
 	if (careersArray.length > 0) {
-		r.push(`${He} has working experience as a ${careersArray.toStringExt()}.`);
+		r.push(`${He} has working experience as a ${toSentence(careersArray)}.`);
 	}
 	return r.join(" ");
 };
diff --git a/src/npc/descriptions/describeBrands.js b/src/npc/descriptions/describeBrands.js
index 5b09e4c5f0f993a325f85e4fb0f7c7aae7f49e05..b88b3a3fa5cccfc67d9c0d46618db88dc899b73d 100644
--- a/src/npc/descriptions/describeBrands.js
+++ b/src/npc/descriptions/describeBrands.js
@@ -25,7 +25,7 @@ App.Desc.brand = function(slave, surface) {
 			} else {
 				r += `${He} also has several unusual brands: `;
 			}
-			r += `${arrayToSentence(array)}. `;
+			r += `${toSentence(array)}. `;
 		} else if (surface) { /* describes a single branded body part */
 			if (surface === "belly" && App.Data.misc.fakeBellies.includes(slave.bellyAccessory) && slave.brand.belly) {
 				r += `${His} fake belly has the same brand, ${slave.brand.belly}, as ${his} real one. `;
diff --git a/src/npc/descriptions/descriptionWidgets.js b/src/npc/descriptions/descriptionWidgets.js
index f0d06c042c40d846cee258682a05a67bcb4cc80b..6073a266a249c40f5eb1a585a0e367fdddc2e9b2 100644
--- a/src/npc/descriptions/descriptionWidgets.js
+++ b/src/npc/descriptions/descriptionWidgets.js
@@ -432,7 +432,7 @@ App.Desc.ageAndHealth = function(slave) {
 				array.push(`could be in <span class="yellow">better condition</span>`);
 			}
 
-			r += ` ${arrayToSentence(array)}.`;
+			r += ` ${toSentence(array)}.`;
 		}
 
 		if (H.tired > 30) {