对象数组按某个属性的某个顺序连续排序

rofer
  • 28

问题描述

按照数据中love这个属性去排序,而且这个顺序必须是“乒乓球、羽毛球、篮球”排序

原始数据如下:

    const data = [
        {
            name: '李',
            love: '篮球'
        },
        {
            name: '李',
            love: '乒乓球'
        },
        {
            name: '李',
            love: '羽毛球'
        },
        {
            name: '张',
            love: '乒乓球'
        },
        {
            name: '张',
            love: '羽毛球'
        },
        {
            name: '张',
            love: '篮球'
        },
        {
            name: '吴',
            love: '羽毛球'
        },    
        {
            name: '吴',
            love: '乒乓球'
        },             

    ]

需要的结果数据如下:

    const data = [
        {
            name: '李',
            love: '乒乓球'
        },
        {
            name: '李',
            love: '羽毛球'
        },
        {
            name: '李',
            love: '篮球'
        },
        {
            name: '张',
            love: '乒乓球'
        },
        {
            name: '张',
            love: '羽毛球'
        },
        {
            name: '张',
            love: '篮球'
        },
        {
            name: '吴',
            love: '乒乓球'
        },    
        {
            name: '吴',
            love: '羽毛球'
        },             

    ]
回复
阅读 332
4 个回答
✓ 已被采纳

从题主给的结果数据来看,并不是单纯的按 love 来排序,而是「先按 name 再按 love 的双重排序」,而且

  • name 不是按字母序,而是按先后出现的顺序
  • love 是按“乒乓球、羽毛球、篮球”的顺序

其中,按 name 出现序需要提前遍历数组,把顺序排出来;或者在排的过程中来产生(因为是顺序,可以在过程中来产生)。

下面这个例子是提前产生的

/**
 * 从给定的字符串数组生成序数映射表
 * @param { string[] } list
 * @returns
 */
function makeSerialsMap(list) {
    return Object.fromEntries(list.map((v, i) => [v, i]));
}

/**
 * 排序
 */
function sort(data) {
    const nameSerials = (() => {
        const dict = new Set();
        const names = data
            .map(({ name }) => {
                if (dict.has(name)) { return undefined; }
                dict.add(name);
                return name;
            })
            .filter(name => name);
        return makeSerialsMap(names);
    })();
    console.log(nameSerials);   // { '李': 0, '张': 1, '吴': 2 }

    // 为 3 个 love 依序关联一个数组,并形成查找表
    const loveSerials = makeSerialsMap(["乒乓球", "羽毛球", "篮球"]);
    console.log(loveSerials);   // { '乒乓球': 0, '羽毛球': 1, '篮球': 2 }

    return data
        .sort((a, b) => {
            // 先按姓名排序(这里暂时没按出现的先后顺序
            const nameDiff = nameSerials[a.name] - nameSerials[b.name];
            if (nameDiff) { return nameDiff; }

            // 然后按 love 对应的序数来排序
            return loveSerials[a.love] - loveSerials[b.love];
        });
}

其中,生成 nameSerials 过程(IIFE 中)中的 names 可以简单地用一句话产生

const names = [...new Set(data.map(({ name }) => name))];

虽然(可能仅在某些环境下)效果还是如预期,但是 Set 本身不应保证排序,所以不同的 JS Runtime 实现有可能不同,所以不这样用。

另一种方法,在过程中产生 nameSerials,其实就是在取序数的时候,检查如果这个 name 不存在于集合中,就把它保存进去,映射到一个“尾值”(一般可以用 Set.prototype.size),并返回。具体代码就不写了,可以自己试试。

你的题目描述似乎存在问题,
如果按 「按照数据中love这个属性去排序,而且这个顺序必须是“乒乓球、羽毛球、篮球”排序」 实现的话

在线代码演示

但按你后面提供的第二份数据你的目的似乎是 「先将数据按 name 排序,然后再按数据中love这个属性去排序,而且这个顺序必须是“乒乓球、羽毛球、篮球”排序」
实现方案如下

在线代码演示

不过这里的排序是 李 吴 张 ,不清楚你第二份数据中的张排第二位是按照什么规则来的,需要你按照自己的规则稍微修改一下

const data = [
                {
                    name: '李',
                    love: '篮球'
                },
                {
                    name: '李',
                    love: '乒乓球'
                },
                {
                    name: '李',
                    love: '羽毛球'
                },
                {
                    name: '张',
                    love: '乒乓球'
                },
                {
                    name: '张',
                    love: '羽毛球'
                },
                {
                    name: '张',
                    love: '篮球'
                },
                {
                    name: '吴',
                    love: '羽毛球'
                },    
                {
                    name: '吴',
                    love: '乒乓球'
                },             

            ]
            const map = data.reduce((map, item) => {
                const { love } = item;
                if (!map.get(love)) {
                    map.set(love, []);
                }
                map.get(love).push(item);
                return map;
            }, new Map());
            const sortLove = ['乒乓球', '羽毛球', '篮球'];
            const length = Math.max(...[...map.values()].map(item => item.length));
            const result = [];
            for(let i = 0; i < length; i++) {
                sortLove.forEach(key => {
                    const item = map.get(key)?.[i];
                    if (item) {
                        result.push(item);
                    }
                })
            }
            console.log(result);
const data = [
      {
        name: "李",
        love: "篮球"
      },
      {
        name: "李",
        love: "乒乓球"
      },
      {
        name: "李",
        love: "羽毛球"
      },
      {
        name: "张",
        love: "乒乓球"
      },
      {
        name: "张",
        love: "羽毛球"
      },
      {
        name: "张",
        love: "篮球"
      },
      {
        name: "吴",
        love: "羽毛球"
      },
      {
        name: "吴",
        love: "乒乓球"
      }
    ];
    let res = [];
    const sorts = ["乒乓球", "羽毛球", "篮球"];
    while (data.length) {
      sorts.forEach(item => {
        const idx = data.findIndex(r => r.love == item);
        idx > -1 && res.push(data.splice(idx, 1));
      });
    }
    console.log(res);
你知道吗?

宣传栏