 		return _idToArticleDictionary
 	}
 
+	private var timelineItemDictionaryNeedsUpdate = true
+	private var _idToTimelineItemDictionary = [String: Int]()
+	private var idToTimelineItemDictionary: [String: Int] {
+		if timelineItemDictionaryNeedsUpdate {
+			rebuildTimelineItemDictionaries()
+		}
+		return _idToTimelineItemDictionary
+	}
+
 	private var sortDirection = AppDefaults.shared.timelineSortDirection {
 		didSet {
 			if sortDirection != oldValue {

	weak var delegate: TimelineModelDelegate?
	
	@Published var nameForDisplay = ""
	@Published var timelineItems = [TimelineItem]()
	@Published var selectedArticleIDs = Set<String>()  // Don't use directly.  Use selectedArticles
	@Published var selectedArticleID: String? = .none  // Don't use directly.  Use selectedArticles
	@Published var selectedArticles = [Article]()

		articleDictionaryNeedsUpdate = false
	}
	
	func rebuildTimelineItemDictionaries() {
		var idDictionary = [String: Int]()
		for (index, timelineItem) in timelineItems.enumerated() {
			idDictionary[timelineItem.article.articleID] = index
		}
		_idToTimelineItemDictionary = idDictionary
		timelineItemDictionaryNeedsUpdate = false
	}
	
	// MARK: Article Fetching
	
	func fetchArticles() {
