如何更高效搜索JavaScript对象以匹配颜色名称或别名?
问题描述
我想要通过颜色的通用名称或别名来匹配颜色。目前的实现是把colorMap拆成16个独立数组,再逐个遍历查找匹配项,代码如下:
if (colorMap) { for (const [key, value] of Object.entries(colorMap)) { // colorMap broken down into arrays let values = Object.values(value) // If the color name is in the array if (values[0].includes(swatch.color)) { // add it to the swatch object swatch.generic.push(key) break; } } }
colorMap的结构为:一个对象,每个键是颜色通用名(如"Red"),对应的值是包含数组的对象,数组内存放该通用名下的所有颜色别名(如["Crimson", "Scarlet"])。
但这个方案速度很慢,且偶尔失效,当存在数百个swatch.color时会直接超时。有没有更高效的搜索方式?
参考完整代码:
import { useMemo } from 'preact/hooks' export const useSwatches = ({ variantColors, colorMap, variantImages, swatchImages, fallbackImage, }) => { return useMemo(() => { if (!variantColors || !swatchImages || !variantImages) { return [] } else { let swatchList = new Set(); let swatches = JSON.parse(swatchImages) let images = JSON.parse(variantImages) let colorMap = JSON.parse(window.Resources.colorMap.textContent) variantColors.forEach(function (color, index) { let swatch = new Object(); swatch.color = color.replaceAll(' ', ' '); swatch.generic = [] swatch.fallback_img = fallbackImage; images.forEach(function (img, index) { if (img.color == swatch.color) { swatch.thumbnail = img.img; } }) if (!swatch.thumbnail) { swatch.thumbnail = fallbackImage } swatches.forEach(function (img, index) { let splitImg = img.split('/'); let swatchMatcher = splitImg[splitImg.length-1]; if (swatchMatcher.split('.')[0] === (swatch.color.replaceAll(' ', '-').toLowerCase())) { swatch.swatch_image = img } }) if (!swatch.swatch_image) { swatch.swatch_image = fallbackImage } if (colorMap) { for (const [key, value] of Object.entries(colorMap)) { // colorMap broken down into arrays let values = Object.values(value) // If the color name is in the array if (values[0].includes(swatch.color)) { // add it to the swatch object swatch.generic.push(key) break; } } } swatchList.add(swatch); }); return swatchList; } }, [ variantColors, colorMap, variantImages, swatchImages, fallbackImage, ]) }
优化方案
核心思路是预构建反向映射表,将所有颜色别名直接映射到对应的通用名,把原来的O(n)遍历查找改成O(1)的对象属性访问,彻底解决超时问题。
步骤1:预处理colorMap生成反向映射
在useMemo内部提前构建别名到通用名的映射表,仅在依赖变化时重新生成:
const aliasToGeneric = useMemo(() => { if (!window.Resources?.colorMap?.textContent) return {}; const parsedColorMap = JSON.parse(window.Resources.colorMap.textContent); const map = {}; for (const [genericName, entry] of Object.entries(parsedColorMap)) { const aliases = Object.values(entry)[0]; aliases.forEach(alias => { map[alias] = genericName; }); } return map; }, [colorMap]);
步骤2:替换原遍历查找逻辑
把原来嵌套的遍历代码直接替换为:
if (aliasToGeneric[swatch.color]) { swatch.generic.push(aliasToGeneric[swatch.color]); }
额外优化:图片查找逻辑的性能提升
原代码中两处图片查找的forEach遍历也可以用同样的思路优化:
- 预构建颜色到缩略图的映射:
const colorToThumbnail = useMemo(() => { if (!variantImages) return {}; const parsedImages = JSON.parse(variantImages); const map = {}; parsedImages.forEach(img => { map[img.color] = img.img; }); return map; }, [variantImages]);
替换原图片查找:
swatch.thumbnail = colorToThumbnail[swatch.color] || fallbackImage;
- 预构建颜色到样本图的映射:
const colorToSwatchImage = useMemo(() => { if (!swatchImages) return {}; const parsedSwatches = JSON.parse(swatchImages); const map = {}; parsedSwatches.forEach(img => { const splitImg = img.split('/'); const swatchName = splitImg[splitImg.length - 1].split('.')[0]; // 转换格式,和swatch.color的处理逻辑保持一致 const colorKey = swatchName.replaceAll('-', ' '); map[colorKey] = img; }); return map; }, [swatchImages]);
替换原样本图查找:
swatch.swatch_image = colorToSwatchImage[swatch.color] || fallbackImage;
完整优化后的代码
import { useMemo } from 'preact/hooks' export const useSwatches = ({ variantColors, colorMap, variantImages, swatchImages, fallbackImage, }) => { // 预构建所有映射表,仅在依赖变化时重新生成 const aliasToGeneric = useMemo(() => { if (!window.Resources?.colorMap?.textContent) return {}; const parsedColorMap = JSON.parse(window.Resources.colorMap.textContent); const map = {}; for (const [genericName, entry] of Object.entries(parsedColorMap)) { const aliases = Object.values(entry)[0]; aliases.forEach(alias => { map[alias] = genericName; }); } return map; }, [colorMap]); const colorToThumbnail = useMemo(() => { if (!variantImages) return {}; const parsedImages = JSON.parse(variantImages); const map = {}; parsedImages.forEach(img => { map[img.color] = img.img; }); return map; }, [variantImages]); const colorToSwatchImage = useMemo(() => { if (!swatchImages) return {}; const parsedSwatches = JSON.parse(swatchImages); const map = {}; parsedSwatches.forEach(img => { const splitImg = img.split('/'); const swatchName = splitImg[splitImg.length - 1].split('.')[0]; const colorKey = swatchName.replaceAll('-', ' '); map[colorKey] = img; }); return map; }, [swatchImages]); return useMemo(() => { if (!variantColors || !swatchImages || !variantImages) { return []; } const swatchList = new Set(); variantColors.forEach(color => { const swatchColor = color.replaceAll(' ', ' '); const swatch = { color: swatchColor, generic: [], fallback_img: fallbackImage, thumbnail: colorToThumbnail[swatchColor] || fallbackImage, swatch_image: colorToSwatchImage[swatchColor] || fallbackImage }; // 匹配通用颜色名 if (aliasToGeneric[swatchColor]) { swatch.generic.push(aliasToGeneric[swatchColor]); } swatchList.add(swatch); }); return swatchList; }, [ variantColors, aliasToGeneric, colorToThumbnail, colorToSwatchImage, fallbackImage, ]); }
优化说明
- 所有映射表通过
useMemo缓存,避免重复计算 - 将O(n)遍历查找全部改为O(1)的对象属性访问,数据量越大性能提升越明显
- 代码结构更简洁,减少嵌套逻辑
内容的提问来源于stack exchange,提问作者Tom Hanks
相关产品推荐
相关产品推荐

