chinese-baby-names
Version:
chinese baby names
1,069 lines (1,046 loc) • 33.7 kB
JavaScript
'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;