Skip to content

Instantly share code, notes, and snippets.

@dievardump
Last active March 20, 2025 14:23
Show Gist options
  • Save dievardump/efdd90818d7dfcdb7e44557e7844d99c to your computer and use it in GitHub Desktop.
Save dievardump/efdd90818d7dfcdb7e44557e7844d99c to your computer and use it in GitHub Desktop.
OpenRarity in JavaScript / js without any dependencies
// code highly inspired by https://github.com/kongnet/openrarity
// rewritten and removed all dep
// the function exported by this file expects data of type Record<string, any>[]
/*
import openRarity from './openRarity.js';
const data = [
{ trait1: value1_1, trait2: value1_2, trait_3: value1_3 },
{ trait1: value2_1, trait2: value2_2, trait_3: value2_3 },
{ trait1: value3_1, trait2: value3_2, trait_3: value3_3 },
];
const scores = openRarity(data);
console.log(scores);
*/
const BLANK = "$$BLANK$$";
function entropy(a) {
return a.reduce((m, n) => m - n * Math.log2(n), 0);
}
function infoContent(a) {
return a.reduce((m, n) => m - Math.log2(n), 0);
}
function countOccurences(a = []) {
const result = {};
for (const item of a) {
result[item] = (result[item] || 0) + 1;
}
return result;
}
function kvw(a = []) {
let length = a.length;
let result = [];
// count the occurences of each items in the list
let occurences = countOccurences(a);
for (let i in occurences) {
result.push({ k: i, v: occurences[i], w: occurences[i] / length });
}
return result;
}
function except(set, exception) {
return set.filter((el) => exception.indexOf(el) == -1);
}
export default function openRarity(data) {
let traitsMap = {};
data.forEach((x) => {
for (let key in x) {
traitsMap[key] = traitsMap[key] ?? [];
x[key].trim();
traitsMap[key].push(x[key]);
}
});
// all trait names / keys
let keys = Object.keys(traitsMap);
// set BLANK value into the traitsMap for attributes that are not on all items
data.forEach((attributes) => {
const notInItem = except(keys, Object.keys(attributes));
notInItem.forEach((attribute) => traitsMap[attribute].push(BLANK));
});
// Create trait value stats include traitEntropy
let traitMapStat = {};
let traitEntropyList = [];
for (let key in traitsMap) {
const values = traitsMap[key];
kvw(values).forEach((x) => {
traitMapStat[key] = traitMapStat[key] || {};
traitMapStat[key][x.k] = x.w;
traitEntropyList.push(x.w);
});
}
// Create token trait probability list
let traitInfoList = [];
data.forEach((i) => {
traitInfoList.push(
keys.map((x) => {
return i[x] ? traitMapStat[x][i[x]] : traitMapStat[x][BLANK];
})
);
});
let entropySum = entropy(traitEntropyList);
let result = traitInfoList.map((x, i) => {
return { v: infoContent(x) / entropySum, tokenId: i + 1 };
});
result.sort((a, b) => b.v - a.v);
// same rank solution
let rankNum = 0;
for (let i = 0; i < result.length; i++) {
rankNum++;
if (result[i].v === result[i - 1]?.v) {
result[i].rank = result[i - 1].rank;
} else {
result[i].rank = rankNum;
}
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment