Last active
March 20, 2025 14:23
-
-
Save dievardump/efdd90818d7dfcdb7e44557e7844d99c to your computer and use it in GitHub Desktop.
OpenRarity in JavaScript / js without any dependencies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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