UNPKG

chinese-baby-names

Version:
1,069 lines (1,046 loc) 33.7 kB
'use strict'; var opencc = require('node-opencc'); var fs = require('fs'); var path = require('path'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs); var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path); function isChinese(char) { if ('\u4e00' <= char && char <= '\u9fa5') { return true; } else { return false; } } /** * 词库 */ exports.PoetryType = void 0; (function (PoetryType) { PoetryType[PoetryType["SHI_JING"] = 1] = "SHI_JING"; PoetryType[PoetryType["CHU_CI"] = 2] = "CHU_CI"; PoetryType[PoetryType["LUN_YU"] = 3] = "LUN_YU"; PoetryType[PoetryType["TANG_SHI"] = 5] = "TANG_SHI"; PoetryType[PoetryType["SONG_SHI"] = 6] = "SONG_SHI"; PoetryType[PoetryType["SONG_CI"] = 7] = "SONG_CI"; })(exports.PoetryType || (exports.PoetryType = {})); /** * 简体转繁体 * @param text * @example * opencc.simplifiedToTraditional('中国') === "中國" * opencc.simplifiedToTraditional('落霞与孤鹜齐飞, 秋水共长天一色') === "落霞與孤鶩齊飛, 秋水共長天一色" * @description * 基于 @see {@link https://github.com/compulim/node-opencc} 本质还是基于 `opencc` { @link https://github.com/byvoid/opencc }, * 但是 `opencc` 安装比较麻烦,所以这里直接使用 `node-opencc` 实现。 */ function simplifiedToTraditional(text) { return opencc.simplifiedToTraditional(text); } /** * 繁体转简体 * @param text */ function traditionalToSimplified(text) { return opencc.traditionalToSimplified(text); } class Name { constructor(name, sentence, title, picks, gender) { this.name = traditionalToSimplified(name); this.sentence = traditionalToSimplified(sentence); this.picks = picks; this.gender = gender; this.title = traditionalToSimplified(title); } toObject() { const { name, sentence, picks, gender, title } = this; return { name, sentence, title, picks, gender, }; } } exports.Gender = void 0; (function (Gender) { Gender["GIRL"] = "girl"; Gender["BOY"] = "boy"; Gender["COMMON"] = "common"; })(exports.Gender || (exports.Gender = {})); function convertGender(chineseGender) { switch (chineseGender.trim()) { case '男': return exports.Gender.BOY; case '女': return exports.Gender.GIRL; default: return exports.Gender.COMMON; } } function shuffle(array) { const shuffled = [...array]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } return shuffled; } /** * 读取本地数据 * @param name */ function readLocalData(name) { const resourcePath = path__namespace.join(__dirname, 'database', name); if (fs__namespace.statSync(resourcePath).isDirectory()) { return []; } if (!fs__namespace.existsSync(resourcePath)) { return []; } const content = fs__namespace.readFileSync(resourcePath, { encoding: 'utf-8' }).split(/\n/); return content; } var StorageType; (function (StorageType) { StorageType["JSON"] = "json"; StorageType["TEXT"] = "text"; StorageType["DAT"] = "dat"; })(StorageType || (StorageType = {})); const PoetCounter = { [exports.PoetryType.TANG_SHI]: 58, [exports.PoetryType.SONG_SHI]: 255, [exports.PoetryType.SONG_CI]: 21, }; const DatabaseStorages = { [exports.PoetryType.SHI_JING]: { type: StorageType.JSON, locate: 'shijing/shijing', }, [exports.PoetryType.CHU_CI]: { type: StorageType.JSON, locate: 'chuci/chuci', }, [exports.PoetryType.LUN_YU]: { type: StorageType.JSON, locate: 'lunyu/lunyu', }, [exports.PoetryType.TANG_SHI]: { type: StorageType.JSON, locate: 'poet.tang/poet.tang.{index}', }, [exports.PoetryType.SONG_SHI]: { type: StorageType.JSON, locate: 'poet.song/poet.song.{index}', }, [exports.PoetryType.SONG_CI]: { type: StorageType.JSON, locate: 'ci.song/ci.song.{index}', }, }; class Database { static getJsonData({ locate, getSentences, getTitle, callback }) { const data = require(`./database/${locate}.json`); for (const item of shuffle(data)) { const sentences = getSentences(item); const title = getTitle(item); for (const content of shuffle(sentences)) { // 转繁体 (不用转繁体了, 直接已经批量转换) // const string = simplifiedToTraditional(content); const isContinue = callback(content.trim(), title); if (isContinue === false) { return; } } } } static generateStrokeDirectory() { const stokes = readLocalData('stroke.dat'); const directory = {}; stokes.forEach((stoke) => { const [, name, count] = stoke.split('|'); directory[name] = parseInt(count); }); return directory; } static generateCharSplitDirectory() { const lines = readLocalData('char-split.dat'); const directory = {}; lines.forEach((line) => { const blocks = line.split(/s/); if (blocks.length < 2) { return; } const [, name, ...sub] = blocks; directory[name] = sub; }); return directory; } static generateNamesDirectory() { const lines = readLocalData('names.dat'); const directory = {}; lines.forEach((line) => { const blocks = line.split(/,/); if (blocks.length < 2) { return; } const [name, gender] = blocks; // 只留名 directory[name.trim().substring(1)] = convertGender(gender); }); return directory; } static generateAINamesDirectory() { const lines = readLocalData('ai-names.dat'); const names = []; lines.forEach((line) => { const blocks = line.split(/\s+/); if (blocks.length < 2) { return; } const [name, gender] = blocks; names.push({ name: name.trim(), gender: convertGender(gender) }); }); return names; } } /** * 拆字字典 */ Database.splitDirectory = Database.generateCharSplitDirectory(); /** * 名字字典 */ Database.namesDirectory = Database.generateNamesDirectory(); /** * 笔画字典 */ Database.strokeDirectory = Database.generateStrokeDirectory(); /** * ai名字字典 */ Database.aiNames = Database.generateAINamesDirectory(); /** * 三才五行吉凶表 * @see {@link http://www.360doc.com/document/22/1020/12/68132707_1052459628.shtml} */ // 大吉 const WUXING_GOODS = [ '木木木', '木木火', '木木土', '木火木', '木火土', '木水木', '木水金', '木水水', '火木木', '火木火', '火木土', '火火木', '火火土', '火土火', '火土土', '火土金', '土火木', '土火火', '土火土', '土土火', '土土土', '土土金', '土金土', '土金金', '土金水', '金土火', '金土土', '金土金', '金金土', '金水木', '金水金', '水木木', '水木火', '水木土', '水木水', '水金土', '水金水', '水水木', '水水金', ]; // 中吉 const WUXING_GENERALS = [ '木火火', '木土火', '火木水', '火火火', '土木木', '土木火', '土土木', '金土木', '金金金', '金金水', '金水水', '水火木', '水土火', '水土土', '水土金', '水金金', '水水水', ]; // 凶 const WUXING_BADS = [ '木木金', '木火金', '木火水', '木土木', '木土水', '木金木', '木金火', '木金土', '木金金', '木金水', '木水火', '木水土', '火木金', '火火金', '火火水', '火金木', '火金火', '火金金', '火金水', '火水木', '火水火', '火水土', '火水金', '火水水', '土木土', '土木金', '土木水', '土火水', '土土水', '土金木', '土金火', '土水木', '土水火', '土水土', '土水水', '金木木', '金木火', '金木土', '金木金', '金木水', '金火木', '金火金', '金火水', '金金木', '金金木', '金水火', '水木金', '水火火', '水火土', '水火金', '水火水', '水土木', '水水土', '水金木', '水金火', '水水火', '水水土', '木木水', '木土金', '火土木', '火土水', '土火金', '金土水', '火金土', '土水金', '金火火', '金火土', '木土土', '金水土', ]; exports.FiveElement = void 0; (function (FiveElement) { FiveElement["GOLD"] = "\u91D1"; FiveElement["WOOD"] = "\u6728"; FiveElement["WATER"] = "\u6C34"; FiveElement["FIRE"] = "\u706B"; FiveElement["EARTH"] = "\u571F"; })(exports.FiveElement || (exports.FiveElement = {})); /** * 五行相生相克关系: * 木生火 火生土 土生金 金生水 水生木 * 木克土 土克水 水克火 火克金 金克木 */ /** * 三才转换为五格 * @param count 笔画数 * @description * 数字1、2为木,3、4为火,5、6为土,7、8为金,9、10为水,三才数理只计 1-10 的数,超过 10 以上的数,只计个位,个位为0,则计为10 * @see {@link http://www.tazzfdc.com/kxnew/160nn9.html} */ function getWuxing(count) { count = count % 10; if (count === 1 || count === 2) { return exports.FiveElement.WOOD; } else if (count === 3 || count === 4) { return exports.FiveElement.FIRE; } else if (count === 5 || count === 6) { return exports.FiveElement.EARTH; } else if (count === 7 || count === 8) { return exports.FiveElement.GOLD; } else if (count === 9 || count === 0) { return exports.FiveElement.WATER; } } /** * 检查三才配置吉 * @param counts 天地人 三才数值 * @param allowGeneral 是否允许中吉 */ function checkSancaiGood(counts, allowGeneral) { const config = getSancaiConfig(counts); if (WUXING_GOODS.includes(config)) { return true; } else if (allowGeneral && WUXING_GENERALS.includes(config)) { return true; } return false; } /** * 获取三才配置 * @param counts 天地人 三才数值 * 对应的就是 转成 金木水 这种 */ function getSancaiConfig(counts) { let config = ''; for (const count of counts) { config += getWuxing(count); } return config; } /** * 获取三才类型 * @param config */ function getSancaiType(config) { if (WUXING_GOODS.includes(config)) { return '大吉'; } else if (WUXING_GENERALS.includes(config)) { return '中吉'; } else if (WUXING_BADS.includes(config)) { return '小吉'; } else { return ''; } } // 最后算出的五格笔画数,被划分为:吉、半吉、凶三组 // 吉 const STROKE_GOODS = [ 1, 3, 5, 6, 7, 8, 11, 13, 15, 16, 17, 18, 21, 23, 24, 25, 29, 31, 32, 33, 35, 37, 39, 41, 45, 47, 48, 52, 57, 61, 63, 65, 67, 68, 81, ]; // 中吉 const STROKE_GENERALS = [27, 38, 42, 55, 58, 71, 72, 73, 77, 78]; // 凶 const STROKE_BADS = [ 2, 4, 9, 10, 12, 14, 19, 20, 22, 26, 28, 30, 34, 36, 40, 43, 44, 46, 49, 50, 51, 53, 54, 56, 59, 60, 62, 64, 66, 69, 70, 74, 75, 76, 79, 80, ]; /** * 81 命理 */ const FATE_COUNT = 81; /** * 计算一个人的三才五格 * @param surname 姓氏 * @param name 名称 */ function calcSancaiWuge(surname, name) { const surnameTraditional = simplifiedToTraditional(surname); const nameTraditional = simplifiedToTraditional(name); const xing1Stroke = getStrokeNumber(surnameTraditional[0]); const xing2Stroke = surnameTraditional && surnameTraditional.length > 1 ? getStrokeNumber(surnameTraditional[1]) : 0; const ming1Stroke = getStrokeNumber(nameTraditional[0]); const ming2Stroke = nameTraditional && nameTraditional.length > 1 ? getStrokeNumber(nameTraditional[1]) : 0; const tian = getTianGe(xing1Stroke, xing2Stroke); const ren = getRenGe(xing1Stroke, xing2Stroke, ming1Stroke); const di = getDiGe(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke); const zong = getZongGe(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke); const wai = getWaiGe(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke); const sancai = getSancaiConfig([tian, ren, di]); return { tian, ren, renStrokeType: getStrokeType(ren), di, diStrokeType: getStrokeType(di), zong, zongStrokeType: getStrokeType(zong), wai, waiStrokeType: getStrokeType(wai), sancai, sancaiType: getSancaiType(sancai), }; } /** * 计算一个人的五格 * * @param xing1Stroke 姓1笔画 * @param xing2Stroke 姓2笔画 * @param _ming1Stroke 名1笔画 * @param _ming2Stroke 名2笔画 */ function calcWuge(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke) { const tian = getTianGe(xing1Stroke, xing2Stroke); const ren = getRenGe(xing1Stroke, xing2Stroke, ming1Stroke); const di = getDiGe(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke); const zong = getZongGe(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke); const wai = getWaiGe(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke); return { tian, ren, di, zong, wai, }; } /** * 天格:单姓的天格是“单姓笔画+1”,复姓的天格是“复姓笔画数相加” * @param xing1Stroke 姓1笔画 * @param xing2Stroke 姓2笔画 * @param _ming1Stroke 名1笔画 * @param _ming2Stroke 名2笔画 * @see {@link https://baike.baidu.com/item/%E6%80%BB%E6%A0%BC%E6%95%B0%E7%90%86/1815675?fromtitle=81%E6%95%B0%E7%90%86&fromid=23720201&fr=aladdin} */ function getTianGe(xing1Stroke, xing2Stroke, _ming1Stroke, _ming2Stroke) { return xing2Stroke === 0 ? xing1Stroke + 1 : xing1Stroke + xing2Stroke; } /** * 单姓的人格是“姓的笔画数+名(第一字)的笔画数”,复姓的人格数理是“复姓的第二个字笔画+名的第一个字笔画”; * @param xing1Stroke 姓1笔画 * @param xing2Stroke 姓2笔画 * @param ming1Stroke 名1笔画 * @param _ming2Stroke 名2笔画 * @see {@link https://baike.baidu.com/item/%E6%80%BB%E6%A0%BC%E6%95%B0%E7%90%86/1815675?fromtitle=81%E6%95%B0%E7%90%86&fromid=23720201&fr=aladdin} */ function getRenGe(xing1Stroke, xing2Stroke, ming1Stroke, _ming2Stroke) { return xing2Stroke === 0 ? xing1Stroke + ming1Stroke : xing2Stroke + ming1Stroke; } /** * 双名的地格是“名字的笔画数相加”,单名的地格是“名的笔画数+1”。 * @param _xing1Stroke 姓1笔画 * @param _xing2Stroke 姓2笔画 * @param ming1Stroke 名1笔画 * @param ming2Stroke 名2笔画 * @see {@link https://baike.baidu.com/item/%E6%80%BB%E6%A0%BC%E6%95%B0%E7%90%86/1815675?fromtitle=81%E6%95%B0%E7%90%86&fromid=23720201&fr=aladdin} */ function getDiGe(_xing1Stroke, _xing2Stroke, ming1Stroke, ming2Stroke) { return ming2Stroke === 0 ? ming1Stroke + 1 : ming1Stroke + ming2Stroke; } /** * 总格:总格是“姓名笔画数的总和”。 * @param xing1Stroke 姓1笔画 * @param xing2Stroke 姓2笔画 * @param ming1Stroke 名1笔画 * @param ming2Stroke 名2笔画 * @see {@link https://baike.baidu.com/item/%E6%80%BB%E6%A0%BC%E6%95%B0%E7%90%86/1815675?fromtitle=81%E6%95%B0%E7%90%86&fromid=23720201&fr=aladdin} */ function getZongGe(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke) { // 归一 const zongGe = xing1Stroke + xing2Stroke + ming1Stroke + ming2Stroke - 1; return (zongGe % 81) + 1; } /** * 单姓“姓名总格减去人格之差再加1”;复姓“将姓名总格减去人格之差”即为外格。 * 单姓单名的外格为2,复姓单名的外格为“总格数理-人格数理+1”。 * @param xing1Stroke 姓1笔画 * @param xing2Stroke 姓2笔画 * @param _ming1Stroke 名1笔画 * @param ming2Stroke 名2笔画 * @see {@link https://baike.baidu.com/item/%E6%80%BB%E6%A0%BC%E6%95%B0%E7%90%86/1815675?fromtitle=81%E6%95%B0%E7%90%86&fromid=23720201&fr=aladdin} */ function getWaiGe(xing1Stroke, xing2Stroke, _ming1Stroke, ming2Stroke) { // 单姓单名 if (xing2Stroke == 0 && ming2Stroke == 0) { return 2; } //单姓复名 if (xing2Stroke == 0 && ming2Stroke != 0) { return 1 + ming2Stroke; } //复姓单名 if (xing2Stroke !== 0 && ming2Stroke == 0) { return xing1Stroke + 1; } //复姓复名 return xing1Stroke + ming2Stroke; } const NumStrokeNumber = { 一: 1, 二: 2, 三: 3, 四: 4, 五: 5, 六: 6, 七: 7, 八: 8, 九: 9, 十: 10, }; /** * * @param surname 姓 * @param allowGeneral 是否允许中吉 */ function getGoodStrokeList(surname, allowGeneral) { const strokeList = []; // 姓氏转繁体 const surnameTraditional = simplifiedToTraditional(surname); const xing1Stroke = getStrokeNumber(surnameTraditional[0]); const xing2Stroke = surname[1] ? getStrokeNumber(surnameTraditional[1]) : 0; /** * 总格数理 * 总格数理(81数理)是日本人熊崎健翁依据姓名的笔画数和一定规则建立起来天格、地格、人格、总格、外格等五格数理关系,并以其所谓的 81 数理,来推算人的各方面运势。 * @see {@link https://baike.baidu.com/item/%E6%80%BB%E6%A0%BC%E6%95%B0%E7%90%86/1815675?fromtitle=81%E6%95%B0%E7%90%86&fromid=23720201&fr=aladdin} */ for (let i = 1; i < FATE_COUNT; i++) { // 可能是单名 if (isGoodStroke(xing1Stroke, xing2Stroke, i, 0, allowGeneral)) { strokeList.push([i, 0]); } // 复名 for (let j = 1; j < FATE_COUNT; j++) { if (isGoodStroke(xing1Stroke, xing2Stroke, i, j, allowGeneral)) { strokeList.push([i, j]); } } } return strokeList; } /** * 判断是否姓和名的笔画数计算是否为吉的笔画数 * @param stroke * @returns * @description 去掉外格的比对,否则单名单字一直为凶。 * [将姓名按笔画数分成五格:天格、人格、地格、总格、外格。三才是指天、人、地格的五行属性,取相生吉,相克凶。] - 百度百科。 * 所以应该取[天地人]即可。 */ function isGoodStroke(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke, allowGeneral) { const wuge = calcWuge(xing1Stroke, xing2Stroke, ming1Stroke, ming2Stroke); const { tian, ren, di, zong } = wuge; // 检测笔画是否为 大吉 // 不同的笔画,被划分为吉、半吉、凶三组 if (STROKE_GOODS.includes(ren) && STROKE_GOODS.includes(di) && STROKE_GOODS.includes(zong)) { // 检查三才 if (checkSancaiGood([tian, ren, di], allowGeneral)) { return true; } } if (allowGeneral && (STROKE_GOODS.includes(ren) || STROKE_GENERALS.includes(ren)) && (STROKE_GOODS.includes(di) || STROKE_GENERALS.includes(di)) && (STROKE_GOODS.includes(zong) || STROKE_GENERALS.includes(zong))) { if (checkSancaiGood([tian, ren, di], allowGeneral)) { return true; } } return false; } function getStrokeType(stroke) { if (STROKE_GOODS.includes(stroke)) { return '大吉'; } else if (STROKE_GENERALS.includes(stroke)) { return '中吉'; } else if (STROKE_BADS.includes(stroke)) { return '小吉'; } else { return ''; } } function getStrokeNumber(word) { let total = 0; for (let char of word) { // 数字需要单独处理 // 名字中有“一、二、三、四、五、六、七、八、九、十”的字要分别按1、2、3、4、5、6、7、8、9、10画 if (NumStrokeNumber[char]) { total += NumStrokeNumber[char]; } else { total += Database.strokeDirectory[char]; } } return getFinalNumber(word, total); } /** * 有一些偏旁部首影响笔画数的计算 * @param word * @param number * @see {@link https://juejin.cn/post/6868186071260856334} */ function getFinalNumber(word, number) { for (let char of word) { if (char in Database.splitDirectory) { const splits = Database.splitDirectory[char]; // "氵"三点水算四画。如:清、洁等 if (splits.includes('氵')) { // 水 number += 1; } // “扌”手旁算四画。如挑、拨等 if (splits.includes('扌')) { // 手 number += 1; } // “月”算肉旁六画。如服、肪、脉等 if (splits[0] === '月') { // 肉 number += 2; } // “艹”算六画。如英、苹、蓉等 if (splits.includes('艹')) { // 艸 number += 3; } // “辶”算七画。如达、迈、迅、过等 if (splits.includes('辶')) { // 辵 number += 4; } // 左“阝”算八画,阳、阴、陈、陆等 if (splits[0] === '阜') { // 左阝 阜 number += 6; } // 右“卩”算七画,如即、邓、邝等 if (splits.includes('邑') && splits.includes('阝')) { // 右阝 邑 number += 5; } // “王”算五画,如琬、珀、玫、瑰等 if (splits[0] === '玉') { // 王字旁 玉 number += 1; } // 礻(示),以示字计为五画 if (splits[0] === '示') { // 礻 示 number += 1; } // 衤(衣),以衣字计为六画 if (splits[0] === '衣') { // 衤 衣 number += 1; } // “犭”算四画。如狄、猛、独等 if (splits[0] === '犭') { // 犭 犬 number += 1; } // 忄(心),竖心旁,以心字计为四画 if (splits[0] === '心') { // 忄 心 number += 1; } } } return number; } function numSequenceRandoms(count) { const arr = []; for (let i = 0; i < count; i++) { arr.push(i); } return shuffle(arr); } class NameGenerator { static batch(config, count) { const instance = new NameGenerator(config, count); return instance.batch(); } constructor(config, count) { this.config = config; this.count = count; this.names = []; this.uniqueNameSet = new Set(); this.singleNameCount = 0; } batch() { return this.pickNames(); } oldBatch() { const sourceList = shuffle(this.config.source); for (let i = 0; i < sourceList.length; i++) { const source = sourceList[i]; switch (source) { case exports.PoetryType.SHI_JING: case exports.PoetryType.CHU_CI: Database.getJsonData({ locate: DatabaseStorages[source].locate, getSentences(item) { return item.content; }, getTitle(item) { return `《楚辞》${item.title} (${item.author}))`; }, callback: (sentence, title) => { return this.checkAndAddNames(sentence, title); }, }); break; case exports.PoetryType.LUN_YU: Database.getJsonData({ locate: DatabaseStorages[source].locate, getSentences(item) { return item.paragraphs; }, getTitle(item) { return `《论语》${item.chapter}))`; }, callback: (sentence, title) => { return this.checkAndAddNames(sentence, title); }, }); break; case exports.PoetryType.TANG_SHI: case exports.PoetryType.SONG_SHI: case exports.PoetryType.SONG_CI: const randoms = numSequenceRandoms(PoetCounter[source]); for (let i = 0; i < randoms.length; i += 1) { Database.getJsonData({ locate: DatabaseStorages[source].locate.replace('{index}', String(randoms[i] * 1000)), getSentences(item) { return item.paragraphs; }, getTitle(item) { return `《${item.rhythmic || item.title}》(${item.author})`; }, callback: (sentence, title) => { return this.checkAndAddNames(sentence, title); }, }); } break; } } const names = this.names.map((name) => name.toObject()); this.names = []; this.uniqueNameSet = new Set(); return names; } pickNames() { const { config } = this; const names = []; const aiNames = Database.aiNames.filter((item) => { if (config.gender === exports.Gender.BOY) { return item.gender === exports.Gender.BOY; } else if (config.gender === exports.Gender.GIRL) { return item.gender === exports.Gender.GIRL; } return true; }); for (let i = 0; i < this.count; i++) { const index = Math.floor(Math.random() * aiNames.length); const { name, gender } = aiNames[index]; names.push({ name, sentence: '', gender, picks: [], title: '', }); } return names; } checkAndAddNames(sentence, title) { const { config } = this; const { goodStrokeList } = config; const charStrokeMap = new Map(); // 先整理出所有的文字笔画的集合 for (let index = 0; index < sentence.length; index++) { const char = sentence[index]; if (isChinese(char)) { const stroke = getStrokeNumber(char); let arr = charStrokeMap.get(stroke); charStrokeMap.set(stroke, Array.isArray(arr) ? [...arr, index] : [index]); } } // 获取满足条件(吉利)的笔画数 for (const goodStroke of goodStrokeList) { // 如果没有存在第一个名的笔画. 直接进行下一次循环 let ming1IndexArray = charStrokeMap.get(goodStroke[0]); if (!ming1IndexArray || ming1IndexArray.length === 0) { continue; } ming1IndexArray = ming1IndexArray.sort((a, b) => a - b); // 单名 if (goodStroke[1] === 0) { for (const index of ming1IndexArray) { const babyName = sentence[index]; if (this.pushName(babyName, sentence, title, [index]) === false) { return false; } } continue; } // 复名 let ming2IndexArray = charStrokeMap.get(goodStroke[1]); if (!ming2IndexArray || ming2IndexArray.length === 0) { continue; } ming2IndexArray = ming2IndexArray.sort((a, b) => a - b); for (let i = 0; i < ming1IndexArray.length; i++) { for (let j = 0; j < ming2IndexArray.length; j++) { // 顺序不能改变 if (ming1IndexArray[i] < ming2IndexArray[j]) { const babyName = sentence[ming1IndexArray[i]] + sentence[ming2IndexArray[j]]; if (this.pushName(babyName, sentence, title, [ming1IndexArray[i], ming2IndexArray[j]]) === false) { return false; } } continue; } } } } checkName(babyName) { const { config } = this; const { minStrokeCount, maxStrokeCount, singleNameWeight } = config; // 单名的权重 if (babyName.length === 1) { if ((this.singleNameCount / this.uniqueNameSet.size) * 100 > singleNameWeight) { return false; } } // 不能跟自己的姓相同 if (config.surname === babyName) { return false; } // 过滤掉笔画 for (let char of babyName.split('')) { const stroke = getStrokeNumber(char); if (stroke < minStrokeCount || stroke > maxStrokeCount) { return false; } } // 过滤掉黑名单数据 if (this.containBadWord(babyName)) { return false; } return true; } pushName(babyName, sentence, title, picks) { const { count, config } = this; // 超出生成的数量限制 if (this.names.length >= count) { return false; } const status = this.checkName(babyName); if (!status) { return; } const gender = Database.namesDirectory[babyName]; if (!gender) { return; } if (config.gender === exports.Gender.GIRL || config.gender === exports.Gender.BOY) { if (gender !== config.gender) { return; } } if (this.uniqueNameSet.has(babyName)) { return; } this.uniqueNameSet.add(babyName); this.names.push(new Name(babyName, sentence, title, picks, gender)); // 单名统计 if (babyName.length === 1) { this.singleNameCount++; } } containBadWord(name) { const { dislikeWords } = this.config; if (!Array.isArray(dislikeWords)) { return false; } for (let char of name) { if (dislikeWords.includes(char)) { return true; } } return false; } } const DEFAULT_MIN_STROKE_COUNT = 3; const DEFAULT_MAX_STROKE_COUNT = 30; const DEFAULT_ALLOW_GENERAL = false; const DEFAULT_GENERATOR_NAMES_COUNT = 1; const DEFAULT_SINGLE_NAME_WEIGHT = 10; class BabyName { static generate(config) { const instance = new BabyName(config); return instance.generate(); } constructor(config) { this.config = Object.assign({ dislikeWords: [], minStrokeCount: DEFAULT_MIN_STROKE_COUNT, maxStrokeCount: DEFAULT_MAX_STROKE_COUNT, allowGeneral: DEFAULT_ALLOW_GENERAL, count: DEFAULT_GENERATOR_NAMES_COUNT, singleNameWeight: DEFAULT_SINGLE_NAME_WEIGHT }, config); } generate() { const { source, surname, allowGeneral, minStrokeCount, maxStrokeCount, dislikeWords, count, singleNameWeight, gender } = this.config; const goodStrokeList = getGoodStrokeList(surname, allowGeneral); const names = NameGenerator.batch({ surname, source, goodStrokeList, minStrokeCount, maxStrokeCount, dislikeWords, singleNameWeight, gender, }, count); return names; } } exports.BabyName = BabyName; exports.calcSancaiWuge = calcSancaiWuge; exports.calcWuge = calcWuge; exports.checkSancaiGood = checkSancaiGood; exports.getDiGe = getDiGe; exports.getGoodStrokeList = getGoodStrokeList; exports.getRenGe = getRenGe; exports.getSancaiConfig = getSancaiConfig; exports.getSancaiType = getSancaiType; exports.getStrokeNumber = getStrokeNumber; exports.getStrokeType = getStrokeType; exports.getTianGe = getTianGe; exports.getWaiGe = getWaiGe; exports.getZongGe = getZongGe; exports.isGoodStroke = isGoodStroke;