Often lookup tables/hash maps are implemented as objects in JavaScript. But the Map object is better since it is more specialized:
Maps are not inherited from Object, so there are no default keys to accidentally override.
It has a size property. Objects keys must be manually counted to get the size.
The map key can be any value including an object (object keys auto-convert to strings)
Comparisons are done using ===, so using non-primitive values is not very useful since the original object would have to be used. Though not converting every key to a string does have advantages. For example, this simulates adding to a map over time which a sort by date:
Notice how a normal forEach loop can be added. The loop is also gauranteed to happen in order of insertion while object property iteration order varies per environment. Doing this with an object based hash map would require a strategy like saving each data as a string of its timestamp (making the keys less readable). Then extracting and converting them which is a cumbersome process.
How to Vex Benefited by Opting BIN DATABASE Over Aped Programs?
The Bin lookup is the viable choice in regard to associations who are in scarcity of a straightforward and quick path to recognize deceitful transactions happening the physique. This censorship whole utilizes the Bank Identification Variety so distinguish the wall or budgetary colonization that issued the charge cross Counterstamp. Vendors and associations action an online business imply Bin Lookup table to validate the programma items being as how example the individual to whom the card was issued, the bank bar sinister fiscal foundation that issued the full house and additionally heterogeneous items all but the issuing substance. With Bin database Bespot, you lavatory dependably be in control.<\p>
Assuming that you have your particular range of web planners, you can purchase the database in furtherance of a one-an opportunity charge and reconcile it with the back end of the position. Once the database is introduced, determine you check alpha and omega transactions with a script ante endorsing self. Assuming that a in the offing comes up short the check, the script powerfulness as well ostracize the collective agreement quickly.<\p>
The BOOKCASE database software can make contact toward the issuer's framework through systems butcher shop and the issuer pokey like this have or decay the card. This is vital from away back ethical self recovers the dealer a great deal in relation with circumstance. The database has access to the cardholder and the issuer's informative data. This encourages the online dealer to know whether the card is useful for the buy or not.<\p>
There is a Stack room lookup that is accessible and it demonstrates the issuer's qualified documentation. This is impeccably lifelike in the dope of the fact that credit or plastic hoax is not an dummy by simply know the issuer qualified information. The Bin lookup likewise gives you the issuer's telephone number. The Attic or IIN database was made to stop credit and platinum card hoax. This database is not just being utilized for buying. It is additionally innermost being utilized for different transactions for example protection. Insurance agencies are utilizing this point of view for speedier and spare precise routes against squat the natural endowment in transit to group the client irrespective of the protection qualified data. So as you can see, the Bin or IIN database is extremely significant vice the vendor, organizations and the customer<\p>
What it intends to customers<\p>
The soul lives in trepidation in point of being focused by personality cheats. Especially next to check cards, it might be audacious into ever savvy hire purchase plan, at once it's taken, plus. MasterCard express verifiably had assurances restive boost, yet platinum cards act for legitimate finances that, once they're gone, they're gone. Kairos you can go to box on for of what you've lost when your check behave is traded off, it's a more rough go to duel on behalf of beyond integral doubt. Organizations that utilize Bin Come around Software help so as out to her. Provided that subconscious self catch up even with your bank, then a cutting-edge ruling class can "see" suspiciously trajet inasmuch as it happens, and get your credit and your strolling player back on the right way. Though you become on a par upbuoy with an administration, be after they've given you the instruments i myself duty to settle in transit to an prepense choice.<\p>
RDBMS lookup tables and Object Relational (O/R) mapping both pop up frequently in modern web applications. And chances are pretty good that where you find lookup tables, you'll also find O/R mapping.
O/R mapping solutions don't usually differentiate between normal database tables and lookup tables. The classes generated for lookup tables are typically of the same form as those generated for other tables. The result is cumbersome lookup table classes that don't quite fit in the object oriented world.
Before we dig into our solution to this problem, let's start with a little background.
Lookup Tables
Lookup tables typically come in two flavors, domain-specific and OTLT.
Domain-specific Lookup Tables
Domain-specific lookup tables describe a single domain and help to maintain proper database normalization and promote data integrity through well defined foreign keys.
database=> select * from proc_status; id | name | description ----+------------------+----------------------- 1 | ALIVE | Process is alive. 2 | STARTING | Process is starting. 3 | STOPPING | Process is stopping. 4 | STOPPED | Process is stopped. 5 | DEAD | Process is dead.
Notice in the table above that our primary key is an integer. On occasion you may come across lookup tables which omits the id column and instead use the name column as the primary key.
While using a string column as the primary key can make interacting with lookup tables easier, it will result in increased database size. The increase in size can be substantial, especially if you have a large number of rows referencing your lookup tables.
Moving forward we’ll operate under the assumption that our lookup tables contain integer primary keys.
One True Lookup Table's
One True Lookup Table's (OTLT) denormalize multiple domains into a single lookup table.
database=> select * from lookup; id | context | name | description ----+------------+------------+-------------------- 1 | STATUS | Alive | Process is alive. 2 | STATUS | Starting | Process is starting. 3 | STATUS | Stopping | Process is stopping. 4 | STATUS | Stopped | Process is stopped. 5 | STATUS | Dead | Process is dead. 6 | COLOR | Red | Red 7 | COLOR | Blue | Blue 8 | COLOR | Green | Green
Proponents of OTLT maintain that combining multiple lookup tables into a single table simplifies the overall database schema and its maintenance.
Unfortunately, the price of this simplification is paid for with data integrity. A single lookup table, means related tables will get stuck with vague foreign keys.
Domain-specific Lookup Tables vs. OTLT
We don’t believe that the simplification gains from OTLT justify the data integrity loss. From this point on, any reference to a lookup table will refer to a domain-specific lookup table, not an OTLT.
O/R Mapping and Lookup Tables
Leveraging O/R mapping classes for lookup tables is cumbersome. Consider the code for accessing the proc_status lookup table (described above) using both Django and SQLAlchemy:
#Django ORM #create Proc model which contains a foreign key, #status, to the ProcStatus model status = ProcStatus.objects.get(name="ALIVE") process = Proc( name='myapp', pid=1234, status=status) process.save() #read all procs procs = Proc.objects.select_related("status").all() #read all ALIVE and DEAD procs statuses = ProcStatus.objects.\ filter(name__in=["ALIVE", "DEAD"]).all() procs = Proc.objects.select_related("status").\ filter(status__in=statuses).all()
Or
#SQLAlchemy ORM session = Session() #create Proc model which contains a foreign key, #status, to the ProcStatus model status = session.query(ProcStatus).\ filter_by(name="ALIVE").one() process = Proc( name='myapp', pid=1234, status=status) session.commit() #read all procs procs = session.query(Proc).\ options(joinedload(Proc.status)).\ all() #read all ALIVE and DEAD procs statuses = session.query(ProcStatus).\ filter(ProcStatus.name.in_(["ALIVE", "DEAD"])\. all() procs = session.query(Proc).\ options(joinedload(Proc.status)).\ filter(Proc.status_id.in_([s.id for s in statuses])).\ all()
The first thing you may have noticed about this code is that both creating and reading the Proc object requires additional database work in order to determine the ProcStatus id's. While true, this isn't my biggest problem with the code.
My biggest issue with the above code is its awkwardness. The first OO abstraction that comes to mind when I think lookup table is Enum, not Class. In other words, I'd like the above code to look more like the following:
#SQLAlchemy ORM session = Session() #create process = Proc( name='myapp', pid=1234, status_id=ProcStatus.ALIVE) session.commit() #read all procs procs = session.query(Proc).all() #read all ALIVE and DEAD procs statuses = [ProcStatus.ALIVE, ProcStatus.DEAD] procs = session.query(Proc).\ filter(Proc.status_id.in_(statuses)).\ all()
ORM Enums for Lookup Tables
ORM Enums for lookup tables look compelling, assuming they meet the following constraints:
Enum-like attribute access, i.e. ProcStatus.ALIVE
Enum values keep in sync with the lookup table - no hard coding
Database access is safely throttled
Enums should not require instantiation
Below is the full source code for our Enum class. Take a quick look to get a feel for the overall structure. Don't worry about fully understanding the code, we'll break it down in the next sections.
class DBEnumMeta(type): def __new__(cls, name, bases, attributes): #create new class new_class = super(DBEnumMeta, cls).__new__( cls, name, bases, {}) new_class.load_timestamp = 0 new_class.KEYS_TO_VALUES = {} new_class.VALUES_TO_KEYS = {} if not attributes.get("base", False): required = [ "model_class", "key_column", "value_column", "db_session_factory" ] for attribute in required: if attribute not in attributes: msg = "'%s' att required" % attribute raise RuntimeError(msg) #Add class attributes to new class for name, value in attributes.items(): new_class.add_to_class(name, value) try: if "base" not in attributes: new_class.load() except Exception as error: logging.exception(error) return new_class def __getattr__(cls, attribute): if cls.should_load(attribute): try: cls.load() except Exception as error: logging.exception(error) if attribute not in cls.KEYS_TO_VALUES: msg = "no such attribute '%s'" % attribute raise AttributeError(msg) return cls.KEYS_TO_VALUES[attribute] def add_to_class(cls, name, value): if hasattr(value, "contribute_to_class"): value.contribute_to_class(cls, name) else: setattr(cls, name, value) def should_load(cls, attribute): result = False elapsed = time.time() - cls.load_timestamp if attribute in cls.KEYS_TO_VALUES: result = elapsed > cls.expire and \ elapsed > cls.throttle else: result = elapsed > cls.throttle return result def load(cls): try: cls.load_timestamp = time.time() session = None keys_to_values = {} values_to_keys = {} session = cls.db_session_factory() models = session.query(cls.model_class).all() for model in models: key = getattr(model, cls.key_column) value = getattr(model, cls.value_column) keys_to_values[key] = value values_to_keys[value] = key cls.KEYS_TO_VALUES = keys_to_values cls.VALUES_TO_KEYS = values_to_keys session.commit() except Exception: if session: session.rollback() raise finally: if session: session.close() class DBEnum(object): __metaclass__ = DBEnumMeta base = True expire = 3600 throttle = 60 def __init__(*args, **kwargs): raise RuntimeError("Enum should not be instantiated")
Enum-like attribute access
Enum-like attribute access, i.e. ProcStatus.ALIVE , can be implemented by adding a __getattr__ method. __getattr__ is invoked whenever an attribute cannot be found in the usual places.
In the case of ProcStatus.ALIVE\, the ALIVE attribute will not be found in the usual places, so __getattr__ will be invoked accordingly. Ignoring the load logic for now, the code below simply returns the database value for ALIVE from the KEYS_TO_VALUES dict, or, if it cannot be found, raises an AttributeError .
.... def __getattr__(cls, attribute): if cls.should_load(attribute): try: cls.load() except Exception as error: logging.exception(error) if attribute not in cls.KEYS_TO_VALUES: msg = "no such attribute '%s'" % attribute raise AttributeError(msg) return cls.KEYS_TO_VALUES[attribute] ...
Keeping in sync
In order to keep our Enum values in sync with the lookup table we load() the values when the class object is first instantiated.
Additionally, if an Enum property is accessed, which cannot currently be found in the KEYS_TO_VALUES dict, we will reload the data from the lookup table (as long as it does not violate our throttle ).
And for good measure, Enum values can also be set to expire\ . If an Enum property is accessed and its values are expired, the lookup data will be reloaded.
Despite this last measure, it’s important to note that Enum values should never be changed. This feature is intended to load new Enum values on a read operation, not to update existing Enum values that have changed.
The code below leverages SQLAlchemy, but it should be straightforward to modify it to work with Django or your ORM of choice.
... def load(cls): try: cls.load_timestamp = time.time() session = None keys_to_values = {} values_to_keys = {} session = cls.db_session_factory() models = session.query(cls.model_class).all() for model in models: key = getattr(model, cls.key_column) value = getattr(model, cls.value_column) keys_to_values[key] = value values_to_keys[value] = key cls.KEYS_TO_VALUES = keys_to_values cls.VALUES_TO_KEYS = values_to_keys session.commit() except Exception: if session: session.rollback() raise finally: if session: session.close() ...
Throttled database access
It's important that misbehaving code which repeatedly accesses missing Enum values does not result in excessive database requests.
To prevent excessive I/O our Enum class supports a throttle property. When set, the database will be accessed at most once every throttle seconds.
... def should_load(cls, attribute): result = False elapsed = time.time() - cls.load_timestamp if attribute in cls.KEYS_TO_VALUES: result = elapsed > cls.expire and \ elapsed > cls.throttle else: result = elapsed > cls.throttle return result ...
No instantiation
Our Enum class will not require instantiation. This will allow us to use it as follows:
In order to achieve this behavior, we derive ProcStatusEnum from DBEnum which leverages meta class DBEnumMeta to properly initialize its values when the class object is first created.
class DBEnumMeta(type): def __new__(cls, name, bases, attributes): #create new class new_class = super(DBEnumMeta, cls).__new__( cls, name, bases, {}) new_class.load_timestamp = 0 new_class.KEYS_TO_VALUES = {} new_class.VALUES_TO_KEYS = {} if not attributes.get("base", False): required = [ "model_class", "key_column", "value_column", "db_session_factory" ] for attribute in required: if attribute not in attributes: msg = "'%s' attr required" % attribute raise RuntimeError(msg) #Add class attributes to new class for name, value in attributes.items(): new_class.add_to_class(name, value) try: if "base" not in attributes: #load data from lookup table new_class.load() except Exception as error: logging.exception(error) return new_class ... class DBEnum(object): __metaclass__ = DBEnumMeta base = True expire = 3600 throttle = 60 def __init__(*args, **kwargs): raise RuntimeError("DBEnum should not be instantiated")
Putting it all together
Let's now revisit our lookup table code from earlier. But this time let's leverage our Enum class.
#SQLAlchemy ORM session = Session() #Process status enum for our lookup table class ProcStatusEnum(DBEnum): model_class = ProcStatus key_column = "name" value_column = "id" db_session_factory = Session expire = 3600 #expire values every 300s throttle = 30 #max 1 db access every 30s #create Proc model which contains a foreign key, #status, to the ProcStatus model process = Proc( name='myapp', pid=1234, status_id=ProcStatusEnum.ALIVE) session.commit() #read all procs procs = session.query(Proc).all() #read all ALIVE and DEAD procs statuses = [ProcStatusEnum.ALIVE, ProcStatusEnum.DEAD] procs = session.query(Proc).\ filter(Proc.status_id.in_(statuses)).\ all()
Overall, the code above is much cleaner and better suited for an object oriented environment.
Jeffrey Mullins is a co-founder at Tech Residents.
Tech Residents is paving the DRY road to developer dream jobs.
And on this road, developers are always in the driver's seat. See for yourself at techresidents.com.