笔记:排序与搜索(初版、非完整使用版)

核心摘要:

排序:根据最新更新的时间进行排序;

搜索:根据关键词搜索,

(1)只要有关键词就是搜索结果之一(非全字匹配);

如:关键词:srty,搜索结果:s23,rcc,rysff,srty等等;

(2)全字匹配关键词,并根据和关键词匹配程度,排序搜索结果

如:关键词:srty,搜索结果:srty,srtykkk,016srty,pouucsrtyLt;等等

关键词匹配程度规则:相似度: +/- [(X.Y)+1];

(1)X表示匹配词里关键词前面字符的个数;

(2)Y表示匹配词里关键词后面字符和个数;

(3)区分大小写,相似度为(1+X.Y),完全匹配,则为1;

(4)不区分大小写,相似度为-(1+X.Y),完全匹配,则为-1;

扫描二维码关注公众号,回复: 15341577 查看本文章

等等

(先记录,待哪天空闲再整理完善记录文档);

1、数据内容格式:

每条数据有唯一id、name、version,

根据name和versionNumber,看是否包含搜索关键词;

存放数据的字典,可快速通过key值定位目标数据和目标物体;

使用列表记录key值,可根据Data里的Time对key值进行排序,遍历列表即可按顺序设置物体;

使用class类记录持久类数据,可方便数据本地存储或与服务器交互存储数据;

使用struct结构,记录轻量、临时数据,方便读取/修改等使用;

使用list列表,方便动态元素的增删修改;

//Data:
[Serializable]
public class Data
{
    public string id { get; set; }
    public string name { get; set; }
    public string versionNumber { get; set; }
    public string leastTime { get; set; }
}
/// <summary>
/// projectItemDatas[index,projectData];
/// index:从0开始
/// </summary>
public Dictionary<int, Data> ProjectDatas = new Dictionary<int, Data>();

//GameObject/Item:
private KeyValuePair<int, Data> curItemData;
public Dictionary<int, RectTransform> projectItemDic = new Dictionary<int, RectTransform>();//int与Item的data在ProjectDatas 里的索引号

//Order:
//根据排序条件(如Data的leastTime值),对字典里的key排序,并用列表记录排序结果
private List<int> projectKeyList = new List<int>();

初始搜索列表:
//当前正在用的PageBtn个数(可能少于等于总PageBtn数,即少于等于pageBtnDic个数)
    private int usingPageBtnCount;
    private List<int> searchList = new List<int>();
优化后搜索列表:
//根据搜索条件(如Data的name和version值),记录包含关键词的Item与关键词的相关程度、所在位置(相似度里大小位置(方便根据相似度进行排序),字典里的key值(方便找到对应Item))
public struct SearchedItem
    {
        /// <summary>
        /// 当前Item匹配关键词,对应的相似度大小
        /// </summary>
        public float similarityValue { get; set; }
        /// <summary>
        /// 当前Item的相似度的大小在列表里排第几
        /// </summary>
        public int similarityIndex { get; set; }
        /// <summary>
        /// 当前Item在字典里的索引值
        /// </summary>
        public int itemIndex { get; set; }
    }
private List<SearchedItem> searchedItems = new List<SearchedItem>();

2、获取/设置数据,并根据数据实例化对应GameObject / Item

//Data    
private  void InitDatas(JArray datas)
    {
        int index = 0;
        foreach (var item in datas)
        {
            Data _datas = new Data();
            string name = item["name"].ToString();
            _projectItemDatas.id = item["id"].ToString();
            _projectItemDatas.versionNumber = item["versionNumber"].ToString();
            string leastTime = item["leastTime"].ToString();
            ProjectDatas.Add(index, _projectItemDatas);
            index++;
        }
    }

private void InitedItem()
    {
        projectItemDic.Clear();
        ClearChilds(projectItemParent);
        foreach (var itemData in ProjectDatas)
        {
            AddProjectItem(itemData);
        }
        projectKeyList.Clear();
        for (int i = 0; i < projectItemDic.Count; i++)
        {
            projectKeyList.Add(i);
        }
    }
/// <summary>
/// 清除父物体下面的所有子物体
/// </summary>
/// <param name="parent"></param>
private void ClearChilds(Transform parent)
    {
        if (parent.childCount > 0)
        {
            for (int i = 0; i < parent.childCount; i++)
            {
                Destroy(parent.GetChild(i).gameObject);
            }
        }
    }
public void AddProjectItem(KeyValuePair<int, Data> itemData)
    {
        RectTransform item = Instantiate(projectItemPrefab, projectItemParent);
        item.Find("Name").GetComponent<TMP_Text>().text = itemData.Value.name;
        item.Find("Version").GetComponent<TMP_Text>().text = itemData.Value.versionNumber;
        item.Find("Time").GetComponent<TMP_Text>().text = itemData.Value.leastTime;
        item.GetComponent<Button>().onClick.AddListener(delegate { ClickProjectItem(itemData); });
        item.gameObject.SetActive(false);
        projectItemDic.Add(itemData.Key, item);
    }

3、根据LeastTime进行排序

public void ClickRankBtn()
    {
        //将所有Item移到临时父物体上
        for (int i = 0; i < projectItemDic.Count; i++)
        {
            projectItemDic[i].SetParent(tempProjectParents);
        }
        //先对key值列表排序,再按顺序依次将目标Item移回projectItemParent上
        if (!isSearch) SetItemOrder(projectKeyList);//对所有数据进行排序
        else SetItemOrder(searchedItems);//对搜索结果列表进行排序
        //将剩下的Item全部移回projectItemParent
        for (int i = 0; i < projectItemDic.Count; i++)
        {
            if (projectItemDic[i].parent != projectItemParent)
                projectItemDic[i].SetParent(projectItemParent);
        }
    }
//排序列表
private void SetItemOrder(List<int> orderList)
    {
        //根据最新时间进行排序
        SortProjectKeyList(orderList);
        for (int i = 0; i < orderList.Count; i++)
        {
            projectItemDic[orderList[i]].SetParent(projectItemParent);
        }
    }

/// <summary>
/// 选择排序指定列表
/// </summary>
/// <param name="itemKeyList"></param>
private void SortProjectKeyList(List<int> itemKeyList)
    {
        for (int i = 0; i < itemKeyList.Count - 1; i++)
        {
            string maxVal = projectItemDic[itemKeyList[i]].Find("Time").GetComponent<TMP_Text>().text;
            int maxIndex = i;
            for (int j = i + 1; j < itemKeyList.Count; j++)
            {
                string nowValue = projectItemDic[itemKeyList[j]].Find("Time").GetComponent<TMP_Text>().text;
                if (!CompareString(maxVal, nowValue))
                {
                    maxVal = nowValue;
                    maxIndex = j;
                }
            }
            int temp = itemKeyList[i];
            itemKeyList[i] = itemKeyList[maxIndex];
            itemKeyList[maxIndex] = temp;
        }
    }

/// <summary>
/// 判断a是否大于等于b
/// </summary>
/// <param name="a">字符串a</param>
/// <param name="b">字符串b</param>
/// <returns></returns>
private bool CompareString(string a, string b)
    {
        int isCompare = 0;
        List<int> aList = GetTimeList(a);
        List<int> bList = GetTimeList(b);
        int len = aList.Count <= bList.Count ? aList.Count : bList.Count;
        for (int i = 0; i < len; i++)
        {
            int index = i;
            if (aList[index] > bList[index]) isCompare = 1;
            if (aList[index] < bList[index]) isCompare = -1;
            if (isCompare != 0) break;
        }
        return (isCompare == -1) ? false : true;
    }
/// <summary>
/// 处理string类型的time,并获得其int类型的list
/// </summary>
/// <param name="a">time格式:“2021/12/30 17:59 am”</param>
/// <returns>返回格式“[2021,12,30,17,59,00]”</returns>
private List<int> GetTimeList(string a)
    {
        List<int> listValue = new List<int>();
        a = Regex.Replace(a, @"\s", ",");//空格转换为“,”
        a = a.Replace(":", ",");
        a = a.Replace("/", ",");
        a = a.Replace("am", "");//删除am字符
        string[] newA = a.Split(',');
        for (int i = 0; i < newA.Length; i++)
        {
            int index = i;
            if (string.IsNullOrEmpty(newA[index])) newA[index] = "00";
            listValue.Add(int.Parse(newA[index]));
        }
        return listValue;
    }

4、获取搜索目标对象

根据名称和版本号搜索:

(1)只要包含关键词里的字符,不一定全字匹配,就都属于目标对象

EG:关键词:测试项目001;搜索结果:测试,001,项目,1,测试1;

private void GetSearchList(string key)
    {
        searchList.Clear();
        if (!string.IsNullOrEmpty(key))
        {
            foreach (var item in projectKeyList)
            {
                string itemName = projectItemDic[item].Find("Name").GetComponent<TextMeshProUGUI>().text;
                string itemVersion = projectItemDic[item].Find("Version").GetComponent<TextMeshProUGUI>().text;
                string itemCharacter = itemName + itemVersion;//该Item里出现的所有字符
                if (ContainKey(key, itemCharacter)) searchList.Add(item);
            }
        }
        else
        {
            foreach (var index in projectKeyList)
            {
                searchList.Add(index);
            }
        }
    }

/// <summary>
/// 判断是否包含搜索目标(不区分大小写)
/// </summary>
/// <param name="key">输入的/查找匹配的关键词</param>
/// <param name="str">和关键词进行匹配的字符串</param>
/// <param name="stringComparison">StringComparison 枚举定义一些规则;OrdinalIgnoreCase 忽略大小写</param>
/// <returns>返回结果:“True”为包含关键词,“False”为不包含关键词</returns>
private bool ContainKey(string key, string str)
    {
        bool isExit = false;
        char[] arrKey = key.ToCharArray();
        for (int i = 0; i < arrKey.Length; i++)
        {
            if (str.Contains(arrKey[i]))
            {
                isExit = true;
                break;
            }
        }
        return isExit;
    }

(2)全字匹配关键词,并根据和关键词匹配程度,排序搜索结果

EG:关键词:测试0;搜索结果:测试0,测试01,项目测试0,项目测试035;

private void GetSearchList(string key)
    {
        searchedItems.Clear();
        if (!string.IsNullOrEmpty(key))
        {
            List<string> itemCharaterList = new List<string>();
            foreach (var item in projectKeyList)
            {
                itemCharaterList.Clear();
                string itemName = projectItemDic[item].Find("Name").GetComponent<TextMeshProUGUI>().text;
                string itemVersion = projectItemDic[item].Find("Version").GetComponent<TextMeshProUGUI>().text;
                itemCharaterList.Add(itemName);
                itemCharaterList.Add(itemVersion);
                //将匹配成功的目标Item存入list,并获取其相似度
                AddContainKeyItemWithSimilarity(itemCharaterList, key, item);
            }
            //根据相似度大小,设置相似度序列
            if (searchedItems.Count > 1)
            {
                for (int i = 0; i < searchedItems.Count; i++)
                {
                    int index = i;
                    ChangeSimilarityIndex(searchedItems[index], index, index);
                }
                SortBySimilarity();
            }
            //对相似度相同的Item根据时间进行排序
            if (searchedItems.Count > 1) SortSearchByTime();
        }
        else
        {
            foreach (var index in projectKeyList)
            {
                SearchedItem item = new SearchedItem();
                item.itemIndex = index;
                item.similarityIndex = index;
                item.similarityValue = index;
                searchedItems.Add(item);
            }
        }
    }

/// <summary>
/// 获取包含关键词的itemIndex,并设置好其相似度
/// </summary>
/// <param name="strList">当前item的搜索范围</param>
/// <param name="key">关键词</param>
/// <param name="itemIndex">当前item的索引值</param>
private void AddContainKeyItemWithSimilarity(List<string> strList, string key, int itemIndex)
    {
        //对搜索范围进行关键词匹配
        foreach (var str in strList)
        {
            if (str.ToLower().Contains(key.ToLower()))//匹配成功
            {
                SearchedItem item = new SearchedItem();
                item.itemIndex = itemIndex;
                item.similarityIndex = itemIndex;
                //获取匹配相似度
                item.similarityValue = GetSimilarity(strList, key);
                searchedItems.Add(item);
                break;
            }
        }
    }

//相似度为 +/-[(X.Y)+1];
//X表示匹配词里关键词前面字符的个数;
//Y表示匹配词里关键词后面字符和个数;
//区分大小写,相似度为(1+X.Y),完全匹配,则为1;
//不区分大小写,相似度为-(1+X.Y),完全匹配,则为-1;
/// <summary>
/// 根据searchedItems已有元素的相似度和该Item的相似程度,获得该Item的相似度
/// </summary>
/// <param name="strList">当前Item的搜索范围</param>
/// <param name="key">搜索关键词</param>
/// <returns>搜索范围与关键词的相似程度值</returns>
private float GetSimilarity(List<string> strList, string key)
    {
        float similarity = 0;//similarity>=1,或<=-1;
        int firstIndex=-1, lastIndex=-1;
        foreach (var str in strList)
        {
            if (str.Contains(key))
            {
                firstIndex = str.IndexOf(key);//Index区分大小写,index>=0;
                lastIndex = str.Length - (firstIndex + key.Length);
                similarity = firstIndex + 1 + lastIndex / 10;
                break;
            }
            else if (str.ToLower().Contains(key.ToLower()))//统一大小写后获取判断
            {
                firstIndex = str.ToLower().IndexOf(key.ToLower());
                lastIndex = str.Length - (firstIndex + key.Length);
                similarity = -(firstIndex + 1 + lastIndex / 10);
                break;
            }
        }
        return similarity;
    }

//冒泡排序
//根据相似值大小,进行排序
//绝对值越大,越不相似,序号越大;索引值从0开始;
//绝对值相同,负值的序号比正值的序号大;
private void SortBySimilarity()
    {
        int temp;//交换标志
        bool exChange;
        for (int i = 0; i < searchedItems.Count; i++)
        {
            exChange = false;
            for (int j = searchedItems.Count -2; j <=i; j--)
            {
                float jValue = Math.Abs(searchedItems[j].similarityValue);
                float jNextValue = Math.Abs(searchedItems[j + 1].similarityValue);
                if (jNextValue == jValue)
                {
                    //负值序号大于正值序号
                    if(searchedItems[j + 1].similarityValue> searchedItems[j].similarityValue)
                    {
                        temp = searchedItems[j + 1].similarityIndex;
                        ChangeSimilarityIndex(searchedItems[j + 1], j + 1, searchedItems[j].similarityIndex);
                        ChangeSimilarityIndex(searchedItems[j], j, temp);
                        exChange = true;
                    }
                }
                else if (jNextValue < jValue)//绝对值越大,序号越大
                {
                    temp = searchedItems[j + 1].similarityIndex;
                    ChangeSimilarityIndex(searchedItems[j + 1], j + 1, searchedItems[j].similarityIndex);
                    ChangeSimilarityIndex(searchedItems[j], j, temp);
                    exChange = true;
                }
            }
            if (!exChange) break;//本趟排序未发生交换,提前终止算法
        }
    }
private void ChangeSimilarityIndex(SearchedItem item,int listIndex,int newSimilarityIndex)
    {
        SearchedItem newItem = new SearchedItem();
        newItem.similarityIndex = newSimilarityIndex;
        newItem.itemIndex = searchedItems[listIndex].itemIndex;
        newItem.similarityValue = searchedItems[listIndex].similarityValue;
        searchedItems[listIndex] = newItem;
    }

/// <summary>
/// 对相似度相同的Item根据时间进行排序
/// </summary>
private void SortSearchByTime()
    {
        //TODO:
    }

注:

(1)这里SearchItem使用list<struct>结构,在初始赋值列表后,之后每次搜索时,不论是计算赋值相似度还是排序相似度,设置相似度索引值,都要进行装箱拆箱操作,就挺麻烦。所以后续直接把struct类型改成class类型,简化操作;

private void ChangeSimilarityIndex(SearchedItem item,int listIndex,int newSimilarityIndex)
    {
        SearchedItem newItem = new SearchedItem();
        newItem.similarityIndex = newSimilarityIndex;
        newItem.itemIndex = searchedItems[listIndex].itemIndex;
        newItem.similarityValue = searchedItems[listIndex].similarityValue;
        searchedItems[listIndex] = newItem;
    }

(2)上面我修改列表里的某个结构体的某个属性,直接简单粗暴的使用新建结构体赋值覆盖的方法。还有其他方法可以修改结构体里的数据,如反射,Object传参(先把结构体转换为object,运行完成后再将object 赋值给 struct)等;最正规的应该是使用ref,out参数,来修改数据成员。(后续再补)。

猜你喜欢

转载自blog.csdn.net/weixin_43908355/article/details/122406060