@@ -170,13 +170,20 @@ interface SectionData {
170170}
171171
172172/**
173- * Check if a value should be displayed (not null, undefined, empty string, or empty array )
173+ * Check if a value should be displayed (not null, undefined, empty string, empty array, or empty object )
174174 * @param value
175175 */
176176const isDisplayableValue = ( value : unknown ) : boolean => {
177177 if ( value === null || value === undefined ) return false ;
178178 if ( typeof value === "string" && value . trim ( ) === "" ) return false ;
179179 if ( Array . isArray ( value ) && value . length === 0 ) return false ;
180+ // Check if object has any displayable properties
181+ if ( typeof value === "object" && ! Array . isArray ( value ) ) {
182+ const entries = Object . entries ( value as Record < string , unknown > ) ;
183+ if ( entries . length === 0 ) return false ;
184+ // Recursively check if any property is displayable
185+ return entries . some ( ( [ , val ] ) => isDisplayableValue ( val ) ) ;
186+ }
180187 return true ;
181188} ;
182189
@@ -220,14 +227,32 @@ const groupFields = (data: Record<string, unknown>): SectionData[] => {
220227 "Other" : { } ,
221228 } ;
222229
230+ // Preprocess: Remove redundant ids.openalex if it matches the main id field
231+ const mainId = typeof data . id === "string" ? data . id . toLowerCase ( ) : null ;
232+ const processedData = { ...data } ;
233+ if ( processedData . ids && typeof processedData . ids === "object" && mainId ) {
234+ const ids = processedData . ids as Record < string , unknown > ;
235+ const openalexId = typeof ids . openalex === "string" ? ids . openalex . toLowerCase ( ) : null ;
236+ if ( openalexId && mainId . includes ( openalexId . split ( "/" ) . pop ( ) ?? "" ) ) {
237+ // Remove openalex from ids since it's redundant with main id
238+ const { openalex : _ , ...remainingIds } = ids ;
239+ if ( Object . keys ( remainingIds ) . length > 0 ) {
240+ processedData . ids = remainingIds ;
241+ } else {
242+ // Delete the key entirely if no other ids remain
243+ delete processedData . ids ;
244+ }
245+ }
246+ }
247+
223248 const identifierKeys = [ "id" , "ids" , "doi" , "orcid" , "issn" , "ror" , "mag" , "openalex_id" , "pmid" , "pmcid" ] ;
224249 const metricKeys = [ "cited_by_count" , "works_count" , "h_index" , "i10_index" , "counts_by_year" , "summary_stats" , "fwci" , "citation_normalized_percentile" , "cited_by_percentile_year" ] ;
225250 const relationshipKeys = [ "authorships" , "institutions" , "concepts" , "topics" , "keywords" , "grants" , "sustainable_development_goals" , "mesh" , "affiliations" , "last_known_institutions" , "primary_location" , "locations" , "best_oa_location" , "alternate_host_venues" , "x_concepts" ] ;
226251 const dateKeys = [ "publication_date" , "publication_year" ] ;
227252 const geoKeys = [ "country_code" , "countries_distinct_count" , "geo" , "latitude" , "longitude" ] ;
228253 const basicKeys = [ "display_name" , "title" , "type" , "description" , "homepage_url" , "image_url" , "thumbnail_url" , "is_oa" , "oa_status" , "has_fulltext" ] ;
229254
230- Object . entries ( data ) . forEach ( ( [ key , value ] ) => {
255+ Object . entries ( processedData ) . forEach ( ( [ key , value ] ) => {
231256 // Skip hidden fields and non-displayable values
232257 if ( HIDDEN_FIELDS . has ( key ) || ! isDisplayableValue ( value ) ) {
233258 return ;
0 commit comments