From 69e59f3e9fff243ba6609fc838ea055bcb5a613a Mon Sep 17 00:00:00 2001 From: Rinke Hoekstra Date: Wed, 6 Nov 2019 15:59:43 +0100 Subject: [PATCH 1/3] Speed up of compare_values and has_value methods by several orders of magnitude. --- lib/pyld/jsonld.py | 60 +++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/lib/pyld/jsonld.py b/lib/pyld/jsonld.py index 023e868..7f5496d 100644 --- a/lib/pyld/jsonld.py +++ b/lib/pyld/jsonld.py @@ -1050,7 +1050,6 @@ def to_rdf(self, input_, options): issuer = IdentifierIssuer('_:b') node_map = {'@default': {}} self._create_node_map(expanded, node_map, '@default', issuer) - # output RDF dataset dataset = {} for graph_name, graph in sorted(node_map.items()): @@ -1151,15 +1150,21 @@ def has_value(subject, property, value): """ if JsonLdProcessor.has_property(subject, property): val = subject[property] - is_list = _is_list(val) - if _is_array(val) or is_list: - if is_list: - val = val['@list'] + # Avoid double checking if val is a list. + # If val is a list, then we treat is as an array (i.e. if value occurs in the list, has_value returns true) + # TODO: is this desirable behavior? + if _is_list(val): + val = val['@list'] + + if _is_array(val): for v in val: - if JsonLdProcessor.compare_values(value, v): - return True + # Avoid in depth comparison if the types are not the same + if type(v) == type(value): + return JsonLdProcessor.compare_values(value, v) # avoid matching the set of values with an array value parameter - elif not _is_array(value): + # TODO: this means that if `value` is an array, there will be no comparison at all and we default to False + # is this desirable behavior? + elif not _is_array(value) and type(val) == type(value): return JsonLdProcessor.compare_values(value, val) return False @@ -1282,28 +1287,33 @@ def compare_values(v1, v2): :return: True if v1 and v2 are considered equal, False if not. """ - # 1. equal primitives - if not _is_object(v1) and not _is_object(v2) and v1 == v2: - if isinstance(v1, bool) or isinstance(v2, bool): - return type(v1) is type(v2) - return True - - # 2. equal @values - if (_is_value(v1) and _is_value(v2) and - v1['@value'] == v2['@value'] and - v1.get('@type') == v2.get('@type') and - v1.get('@language') == v2.get('@language') and - v1.get('@index') == v2.get('@index')): - if isinstance(v1['@value'], bool) or isinstance(v2['@value'], bool): - return type(v1['@value']) is type(v2['@value']) + # 1. equal primitives (= equal anything) + # This should just be equality... + # The previous version also returned true if one of the value types was bool and they were equal + # but they should only be equal if the types correspond as well. + # If they are both objects and they are equal in all respects, then they *are* equal (even if they are not primitives) + if v1 == v2: return True # 3. equal @ids - if (_is_object(v1) and '@id' in v1 and - _is_object(v2) and '@id' in v2): - return v1['@id'] == v2['@id'] + # equal @ids only compares on one key, and is therefore preferred to do + # so let's do that first, and then only if there's a key error (i.e. no '@id'), we assume it's a value comparison. + try: + # If v1 and v2 have the same @id, they are the same + v1['@id'] == v2['@id'] + except KeyError: + # if key error, then it is indeed a dict, but a literal value, not an object. + try: + return v1['@value'] == v2['@value'] and v1.get('@type') == v2.get('@type') and v1.get('@language') == v2.get('@language') and v1.get('@index') == v2.get('@index') + except: + # It is a dictionary, but a regular JSON one, not a JSON-LD dictionary + return False + except: + # one of v1 and v2 is not a dictionary + return False + # The two values are not the same. return False @staticmethod From 0ab119055f43db5e10d96c78cbf362b51a10dc33 Mon Sep 17 00:00:00 2001 From: Miel Vander Sande Date: Fri, 27 Feb 2026 11:08:16 +0100 Subject: [PATCH 2/3] Simplify and speedup compare_values --- lib/pyld/jsonld.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/pyld/jsonld.py b/lib/pyld/jsonld.py index 7f5496d..13831dd 100644 --- a/lib/pyld/jsonld.py +++ b/lib/pyld/jsonld.py @@ -1287,31 +1287,31 @@ def compare_values(v1, v2): :return: True if v1 and v2 are considered equal, False if not. """ - - # 1. equal primitives (= equal anything) - # This should just be equality... - # The previous version also returned true if one of the value types was bool and they were equal - # but they should only be equal if the types correspond as well. - # If they are both objects and they are equal in all respects, then they *are* equal (even if they are not primitives) - if v1 == v2: + # 0. Quick identity check (Performance boost) + if v1 is v2: return True + # Helper for strict type checking (prevents 1 == True) + def _strict_eq(a, b): + return a == b and (type(a) is type(b) if isinstance(a, bool) or isinstance(b, bool) else True) + + # 1. equal primitives + if not _is_object(v1) and not _is_object(v2): + return _strict_eq(v1, v2) + + # 2. equal @values + if _is_value(v1) and _is_value(v2): + # Using tuples is faster than using and + t1 = (v1.get('@type'), v1.get('@language'), v1.get('@index')) + t2 = (v2.get('@type'), v2.get('@language'), v2.get('@index')) + + return t1 == t2 and _strict_eq(v1['@value'], v2['@value']) + # 3. equal @ids - # equal @ids only compares on one key, and is therefore preferred to do - # so let's do that first, and then only if there's a key error (i.e. no '@id'), we assume it's a value comparison. - try: - # If v1 and v2 have the same @id, they are the same - v1['@id'] == v2['@id'] - except KeyError: - # if key error, then it is indeed a dict, but a literal value, not an object. - try: - return v1['@value'] == v2['@value'] and v1.get('@type') == v2.get('@type') and v1.get('@language') == v2.get('@language') and v1.get('@index') == v2.get('@index') - except: - # It is a dictionary, but a regular JSON one, not a JSON-LD dictionary - return False - except: - # one of v1 and v2 is not a dictionary - return False + if _is_object(v1) and _is_object(v2): + # If both are objects, try to get the @id + id1, id2 = v1.get('@id'), v2.get('@id') + return id1 == id2 if id1 is not None else False # The two values are not the same. return False From 181e4de4905a8ee68015842cfe3b6ff0be2e115b Mon Sep 17 00:00:00 2001 From: Miel Vander Sande Date: Mon, 2 Mar 2026 10:32:30 +0100 Subject: [PATCH 3/3] Simplify and speedup has_value --- lib/pyld/jsonld.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/pyld/jsonld.py b/lib/pyld/jsonld.py index 13831dd..d75c332 100644 --- a/lib/pyld/jsonld.py +++ b/lib/pyld/jsonld.py @@ -1148,24 +1148,31 @@ def has_value(subject, property, value): :return: True if the value exists, False if not. """ + # Localize the method lookup to avoids repeated class-attribute resolution in the loop/logic and increase performance + compare = JsonLdProcessor.compare_values + if JsonLdProcessor.has_property(subject, property): val = subject[property] + + # 1. Normalize @list objects # Avoid double checking if val is a list. # If val is a list, then we treat is as an array (i.e. if value occurs in the list, has_value returns true) - # TODO: is this desirable behavior? if _is_list(val): val = val['@list'] + # 2. Handle Collection (Array/List) if _is_array(val): - for v in val: - # Avoid in depth comparison if the types are not the same - if type(v) == type(value): - return JsonLdProcessor.compare_values(value, v) + # 'any' with a localized function is the fastest way to loop in Python + return any(compare(value, v) for v in val) + + # 3. Handle Single Value # avoid matching the set of values with an array value parameter - # TODO: this means that if `value` is an array, there will be no comparison at all and we default to False - # is this desirable behavior? - elif not _is_array(value) and type(val) == type(value): - return JsonLdProcessor.compare_values(value, val) + # TODO: If the parameter 'value' is an array, there will be no comparison at all if `value` is an array and + # we default to False. Hence, has_value usually returns False unless comparing against another array + # (which is rare here). Is this desirable behavior? + if not _is_array(value): + return compare(value, val) + return False @staticmethod