diff --git a/lib/pyld/jsonld.py b/lib/pyld/jsonld.py index 023e868..d75c332 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()): @@ -1149,18 +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] - is_list = _is_list(val) - if _is_array(val) or is_list: - if is_list: - val = val['@list'] - for v in val: - if JsonLdProcessor.compare_values(value, v): - return True + + # 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) + if _is_list(val): + val = val['@list'] + + # 2. Handle Collection (Array/List) + if _is_array(val): + # '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 - elif not _is_array(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 @@ -1282,28 +1294,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) + # 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) 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']) - return True + 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 - if (_is_object(v1) and '@id' in v1 and - _is_object(v2) and '@id' in v2): - return v1['@id'] == v2['@id'] + 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 @staticmethod