@@ -100,6 +100,37 @@ let resolve_conflicts duplicate_group =
100100
101101 { base_entry with Bibtex. contents = merged_contents }
102102
103+ (* Merge entries non-interactively by keeping first occurrence of each field *)
104+ let merge_entries_non_interactive entries =
105+ if entries = [] then invalid_arg " merge_entries_non_interactive: empty list"
106+ else
107+ let base_entry = List. hd entries in
108+ let merged_fields = Hashtbl. create 16 in
109+
110+ (* Collect all fields, keeping first occurrence *)
111+ List. iter
112+ (fun entry ->
113+ List. iter
114+ (function
115+ | Bibtex. Field { name; value } ->
116+ let name_lower = String. lowercase_ascii name in
117+ if not (Hashtbl. mem merged_fields name_lower) then
118+ Hashtbl. replace merged_fields name_lower
119+ (name, Bibtex. string_of_field_value value)
120+ | Bibtex. EntryComment _ -> () )
121+ entry.Bibtex. contents)
122+ entries;
123+
124+ (* Build merged entry *)
125+ let merged_contents =
126+ Hashtbl. fold
127+ (fun _ (name , value ) acc -> Bibtex. make_field name value :: acc)
128+ merged_fields []
129+ |> List. rev
130+ in
131+
132+ { base_entry with Bibtex. contents = merged_contents }
133+
103134(* Main deduplication function with IO *)
104135let deduplicate_entries ?(keys = [ " title" ; " author" ; " year" ])
105136 ?(interactive = true ) entries =
@@ -128,7 +159,7 @@ let deduplicate_entries ?(keys = [ "title"; "author"; "year" ])
128159 List. map
129160 (fun group ->
130161 if interactive then resolve_conflicts group
131- else Bibtex. merge_entries_non_interactive group.Bibtex. entries)
162+ else merge_entries_non_interactive group.Bibtex. entries)
132163 duplicate_groups
133164 in
134165
0 commit comments