高级前端
手写代码
【Q691】如何实现一个 ORM 类似的 find 链式调用

如何实现一个 ORM 类似的 find 链式调用

更多描述 如下代码所示,使用 find 函数实现链式调用

const data = [
  {userId: 8, title: 'title1'},
  {userId: 11, title: 'other'},
  {userId: 15, title: null},
  {userId: 19, title: 'title2'}
];
 
// 查找data中,符合where中条件的数据,并根据orderBy中的条件进行排序
const result = find(data).where({
  "title": /\d$/   // 这里意思是过滤出数组中,满足title字段中符合 /\d$/的项
}).orderBy('userId', 'desc');  // 这里的意思是对数组中的项按照userId进行倒序排列
 
//=> 返回 [{ userId: 19, title: 'title2'}, { userId: 8, title: 'title1' }];
console.log(result.value);
 
 
> Issue
 欢迎在 Gtihub Issue 中回答此问题: [Issue 712](https://github.com/shfshanyue/Daily-Question/issues/712)
 
 
> Author
回答者: [shfshanyue](https://github.com/shfshanyue)
 
 
代码见 codepen,[如何实现链式调用](https://codepen.io/shanyue/pen/abWQLZv?editors=0012)
 
``` js
function find (data) {
  return {
    data,
    where (match) {
      this.data = this.data.filter((item) => {
        return Object.entries(match).every(([key, value]) => {
          if (value instanceof RegExp) {
            return value.test(item[key])
          }
          return item[key] === value
        })
      })
      return this
    },
 
    orderBy (key, type) {
      this.data.sort((x, y) => type !== 'desc' ? x[key] - y[key] : y[key] - x[key])
      return this
    }
  }
}

Author 回答者: haotie1990 (opens in a new tab)

function find(data) {
  class FindManager {
    constructor(data) {
      this.data = data;
    }
    where(query) {
      const data = [...this.data];
      return new FindManager(
        data.filter((item) => {
          return Object.entries(query).every(([key, filter]) => {
            if (
              Object.prototype.toString.call(filter).slice(8, -1) === "Regex"
            ) {
              return filter.test(item[key]);
            } else {
              return filter === item[key];
            }
          });
        }),
      );
    }
    orderBy(key, order) {
      const data = [...this.data];
      data.sort((a, b) => {
        return order === "asc" ? a[key] - b[key] : b[key] - a[key];
      });
      return new FindManager(data);
    }
    get value() {
      return this.data;
    }
  }
  return new FindManager(data);
}

Author 回答者: heretic-G (opens in a new tab)

function find(data) {
  const temp = Array.isArray(data) ? [...data] : { ...data };
  let opt = {
    where: function where(opt) {
      return find(
        Object.entries(opt).reduce((prev, [key, match]) => {
          return prev.filter((curr) => {
            return match.test(curr[key]);
          });
        }, this),
      );
    },
    orderBy: function order(key, type) {
      return this.sort((prev, next) => {
        switch (type) {
          case "desc":
            return next - prev;
          case "asc":
            return prev - next;
          default:
            return prev - next;
        }
      });
    },
  };
  Object.setPrototypeOf(opt, Array.prototype);
  Object.setPrototypeOf(temp, opt);
  return temp;
}

Author 回答者: vandvassily (opens in a new tab)

// 综合一下两位大佬的写法
class Find {
  constructor(data) {
    this.data = [...data];
  }
 
  where(query) {
    this.data = this.data.filter((item) => {
      return Object.entries(query).every(([key, value]) => {
        if (value instanceof RegExp) {
          return value.test(item[key]);
        } else {
          return item[key] === value;
        }
      });
    });
 
    return this;
  }
 
  orderBy(key, type) {
    this.data = this.data.sort((a, b) =>
      type !== "desc" ? a[key] - b[key] : b[key] - a[key],
    );
 
    return this;
  }
 
  get value() {
    return this.data;
  }
}
 
function find(data) {
  return new Find(data);
}