Skip to content

Instantly share code, notes, and snippets.

@panzi
Last active September 8, 2021 03:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panzi/9855265a36a85be785ebecea37bccd27 to your computer and use it in GitHub Desktop.
Save panzi/9855265a36a85be785ebecea37bccd27 to your computer and use it in GitHub Desktop.
Compare values using JavaScript semantics in Python. Not sure if I got everything right. This is more of a demonstration that JavaScript equality semantics are crazy.
# Copyright 2021 Mathias Panzenböck
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# dict is a stand-in for all plain JavaScript objects in this.
def js_equals(a, b) -> bool:
if type(a) is type(b):
# To be more strict it should be `a is b` if they're list or dict
# (i.e. objects in JavaScript).
return a == b
if isinstance(a, (int, float)):
if isinstance(b, (int, float)):
return a == b
return a == to_float(b)
if isinstance(b, (int, float)):
return to_float(a) == b
if a is None or b is None:
return False
if isinstance(a, str):
if isinstance(b, bool):
return to_float(a) == to_float(b)
if isinstance(b, (list, dict)):
return a == to_string(b)
raise TypeError
if isinstance(a, bool):
return to_float(a) == to_float(b)
if isinstance(a, (list, dict)):
if isinstance(b, (list, dict)):
return False
if isinstance(b, str):
return to_string(a) == b
if isinstance(b, bool):
return to_float(a) == to_float(b)
raise TypeError
raise TypeError
def to_float(a) -> float:
if a is None:
return 0.0
if isinstance(a, list):
if not a:
return 0.0
elif len(a) > 1:
return float('nan')
else:
return to_float(a[0])
if isinstance(a, dict):
return float('nan')
try:
# XXX: This accepts a little bit too much. I.e. it will allow
# 'inf', '+inf', and '-inf', where JavaScript only allows
# 'Infinity', '+Infinity', and '-Infinity' (which this
# also allows).
return float(a)
except ValueError:
return float('nan')
def to_string(a) -> str:
if isinstance(a, str):
return a
if isinstance(a, (int, float)):
return str(a)
if a is None:
return 'null'
if a is True:
return 'true'
if a is False:
return 'false'
if isinstance(a, list):
return ','.join(to_string(b) for b in a)
if isinstance(a, dict):
return '[object Object]'
# Note that this calls a.toString() in JavaScript.
# Meaning it returns something different for any object in
# JavaScript. E.g. for Date it returns something like:
# "Wed Sep 08 2021 05:57:22 GMT+0200 (Central European Summer Time)"
# Meanig you can compare a date with a string formated like
# that and if the date would be formatting to the string it
# yields true.
raise TypeError
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment