fezaoduke / fe-practice-hard

晚练课
69 stars 6 forks source link

第 1 期(2019-05-08):多重排序 #2

Open wingmeng opened 5 years ago

wingmeng commented 5 years ago

这是前端晚练课的第 1 期,希望大家在参与前先去「达成以下 2 个成就」:

  1. 有看过 「前端晚练课的简介、愿景、角色、参与方式和激励措施」
  2. 有阅读过 「答题规范」

来源:原创题 难度:★★

封装一个多重排序函数 multiSort,功能要求:

  1. 接收一个对象数组,返回一个按多个字段名顺序排序后的新数组;

  2. 可自定义排序参照的字段名顺序;

    例如:['field2', 'field1', 'field3']

    首先按 field2 进行排序,如果 field2 的值相等,则按 field1 排序;如果 field1 的值仍相等,则按 field3 排序……

  3. 可设置排序规则为正序(升序)或倒序(降序),默认为正序。

实战用例:

根据员工数据,评选出月度最佳员工

[
  { "name": "Sweet", "education": 4, "sales": 10, "duty": 24, "workAge": 2 },
  { "name": "Tough", "education": 3, "sales": 15, "duty": 24, "workAge": 6 },
  { "name": "Yummy", "education": 4, "sales": 10, "duty": 24, "workAge": 4 },
  { "name": "Ghost", "education": 6, "sales": 15, "duty": 23, "workAge": 8 },
  { "name": "Flora", "education": 5, "sales": 15, "duty": 24, "workAge": 6 }
]

销售额(sales)、出勤天数(duty)、工龄(workAge)、教育程度(education) 的顺序 降序 排名:

[
  { "name": "Flora", "education": 5, "sales": 15, "duty": 24, "workAge": 6 },
  { "name": "Tough", "education": 3, "sales": 15, "duty": 24, "workAge": 6 },
  { "name": "Ghost", "education": 6, "sales": 15, "duty": 23, "workAge": 8 },
  { "name": "Yummy", "education": 4, "sales": 10, "duty": 24, "workAge": 4 },
  { "name": "Sweet", "education": 4, "sales": 10, "duty": 24, "workAge": 2 }
]

参考答案:

function multiSort(arr, rules, reversal) {
  return [...arr].sort((o, p) => {
    let a, b;

    for (let rule of rules) {
      a = o[rule];
      b = p[rule];

      if (a !== b) {
        return (a - b) * (reversal ? -1 : 1);
      }
    }
  });
}

// 或者挂载到 Array 构造函数的原型上
Array.prototype.multiSort = function(rules, reversal) {
  return [...this].sort((o, p) => {
    // 逻辑代码同上,此处略
  });
}

本期优秀回答者: @cnyballk

liwenkang commented 5 years ago
var list = [
    {"name": "Sweet", "education": 4, "sales": 10, "duty": 24, "workAge": 2},
    {"name": "Tough", "education": 3, "sales": 15, "duty": 24, "workAge": 6},
    {"name": "Yummy", "education": 4, "sales": 10, "duty": 24, "workAge": 4},
    {"name": "Ghost", "education": 6, "sales": 15, "duty": 23, "workAge": 8},
    {"name": "Flora", "education": 5, "sales": 15, "duty": 24, "workAge": 6}
]

var fieldArray = ['sales', 'duty', 'workAge', 'education']

function multiSort(data, fieldArray, sortMethod = 'ascendingOrder') {
    // 为了不修改原数组,使用了 data.slice()
    return data.slice().sort((a, b) => {
        for (var i = 0; i < fieldArray.length; i++) {
            if (b[fieldArray[i]] !== a[fieldArray[i]]) {
                if (sortMethod === 'descendingOrder') {
                    // 降序
                    return b[fieldArray[i]] - a[fieldArray[i]]
                } else if (sortMethod === 'ascendingOrder') {
                    // 升序
                    return a[fieldArray[i]] - b[fieldArray[i]]
                }
            }
        }
    })
}

multiSort(list, fieldArray, 'descendingOrder')
cnyballk commented 5 years ago
const multiSort = (dataSource=[], filters=[], invertedOrder = false) =>
  [...dataSource].sort((a, b) => {
    for (let i of filters) {
      if (a[i] !== b[i]) {
        return invertedOrder ? a[i] < b[i] : a[i] > b[i];
      }
    }
  });
limoning commented 5 years ago

/**
 * 多重排序
 * @param {Array} arr 要排序的数组
 * @param {Array} sortField 排序字段 
 * @param {Boolean} isAscending 是否升序 默认为是
 */
const multiSort = (arr, sortField, isAscending = true) => {
  // 如果没有排序字段直接返回原数组顺序的新数组
  if (!sortField || !sortField.length) {
    return [...arr]
  }
  const len = sortField.length
  return [...arr].sort((obj1, obj2) => {
    let index = 0
    // 循坏找出一个不相等的排序字段的下标
    while (obj1[sortField[index]] === obj2[sortField[index]] && index < len) {
      index++
    }
    // 如果下标等于排序字段数组长度则表示所有字段都相等直接返回0
    if (index === len) {
      return 0
    } else {
      return isAscending
        ? obj1[sortField[index]] - obj2[sortField[index]]
        : obj2[sortField[index]] - obj1[sortField[index]]
    }
  })
}
ahao430 commented 5 years ago
const multiSort = (data, conditions, order = 'asc') => {
  let len = conditions.length
  const getOrder = (a, b, index) => {
    let condition = conditions[index]
    if (a[condition] === b[condition] && index < len - 1) {
      return getOrder(a, b, index + 1)
    } else {
      return order === 'desc' ? b[condition] - a[condition] : a[condition] - b[condition]
    }
  }

  return data.sort((a, b) => {
    return getOrder(a, b, 0)
  })
}