Penyaringan data deklaratif di bagian depan. Js / ts


Apakah Anda sering harus menulis penangan filter untuk data Anda? Ini bisa berupa array untuk menggambar tabel, kartu, daftar - apa saja.


Ketika penyaringan bersifat statis, maka semuanya sederhana. Fitur standar map, filterdan reducecukup cukup. Tetapi bagaimana jika data memiliki struktur yang kompleks atau bersarang, dan mungkin ada banyak aturan untuk filter. Aturan dapat diulang, data dapat diubah, dan semakin banyak kontrol filter muncul, semakin kompleks dan tidak stabil kode handler akan.


Bagaimana mengatasi masalah meningkatnya kompleksitas?


Saya sendiri menghadapi masalah ini saat mengembangkan aplikasi yang bekerja dengan sejumlah besar data. Secara bertahap ditambahkan semakin banyak filter baru.


, . . , . , . - .


, .
, :


  1. ng-table โ€” . .
  2. List.js โ€” ng-table, .
  3. filter.js โ€” , , .
  4. Isotope โ€” DOM . .

, :


  1. .
  2. .
  3. .
  4. , and or .
  5. . .
  6. ( , , , , , )

awesome-data-filter.


, , .



.


npm install awesome-data-filter

.
, :


const users = [
  {
    age: 31,
    name: "Marina Gilmore",
  },
  {
    age: 34,
    name: "Joyner Mccray",
  },
  {
    age: 23,
    name: "Inez Copeland",
  },
  {
    age: 23,
    name: "Marina Mitchell",
  },
  {
    age: 25,
    name: "Prince Spears",
  },
];

:


const filterValue = {
  age: 23,
  searchText: "mari",
};

, .


:


  • matchText โ€” ;
  • equalProp โ€” ;
    • betweenDates โ€” ;
    • equalOneOf โ€” ;
    • someInArray โ€” ;
    • isEmptyArray โ€” ;
    • lessThen โ€” , ;
    • moreThen โ€” , ;
    • not โ€” .

matchText equalProp.


:


  • filterField โ€” ;
  • elementField โ€” .

import { 
  buildFilter, 
  elementField, 
  filterField, 
} from "awsome-data-filter";
import { matchText, equalProp } from "awsome-data-filter/rules";
import { and } from "awsome-data-filter/conditions";

const filter = buildFilter({
    rules: {
      elementFilter: and([
        matchText(filterField("searchText"), elementField("name")),
        equalProp(filterField("age"), elementField("age")),
      ]),
    },
  });

const { elements } = filter(
    filterValue,
    {
      groups: [],
      elements: users,
    },
);

console.log(elements);
// elements: [{ age: 23, name: "Marina Mitchell" }]

filter groups elements.


, . .



, , elements.


and or, 2 .


import { 
  buildFilter, 
  elementField, 
  filterField, 
} from "awsome-data-filter";
import { matchText, equalProp } from "awsome-data-filter/rules";
import { or } from "awsome-data-filter/conditions";

const filter = buildFilter({
    rules: {
      elementFilter: or([
        matchText(filterField("searchText"), elementField("name")),
        equalProp(filterField("age"), elementField("age")),
      ]),
    },
  });

const { elements } = filter(
    filterValue,
    {
      groups: [],
      elements: users,
    },
);

console.log(elements);
// elements: 
// [
//   {
//     age: 31,
//     name: "Marina Gilmore",
//   },
//   {
//     age: 23,
//     name: "Inez Copeland",
//   },
//   {
//     age: 23,
//     name: "Marina Mitchell",
//   }
// ]

filterField, elementField .


constValue .
or(..., matchText, [and([..., matchText, ...]), or([..., ...])])


. :


const dataList = [
  {
    groupName: "first group",
    list: [
      { age: 31, name: "Marina" },
      { age: 23, name: "Fabio" },
    ],
  },
  {
    groupName: "second group",
    groups: [
      {
        groupName: "third group",
        list: [],
        groups: [
          {
            groupName: "fourth group",
            list: [{ age: 42, name: "Li" }],
          },
        ],
      },
    ],
    list: [
      { age: 41, name: "Marina" },
      { age: 29, name: "Inez" },
      { age: 33, name: "Marina" },
    ],
  },
  {
    groupName: "fifth group",
    list: [
      { age: 21, name: "Dmitriy" },
      { age: 22, name: "Li" },
      { age: 45, name: "Mitchell" },
    ],
  },
];

traversal:


import { 
  buildFilter, 
  elementField, 
  filterField, 
} from "awsome-data-filter";
import { matchText } from "awsome-data-filter/rules";

const filter = buildFilter({
    traversal: {
      getChildrenFunc: group => group.list, //    
      setChildrenFunc: (group, list) => ({ ...group, list }), //      
      getGroupsFunc: group => group.groups, //    
      setGroupsFunc: (group, groups) => ({ ...group, groups }), //    
    },
    rules: {
      elementFilter: matchText(filterField("searchText"), elementField("name")),
    },
  });

const filterValue = {
  searchText: "li",
};

const { groups } = filter(filterValue, {
  groups: dataList, //      
  elements: [], //       
});

console.log(groups);
// groups: 
//[
//  {
//    groupName: "second group",
//    groups: [
//      {
//        groupName: "third group",
//        list: [],
//        groups: [
//          {
//            groupName: "fourth group",
//            list: [{ age: 42, name: "Li" }],
//          },
//        ],
//      },
//    ],
//    list: [],
//  },
//  {
//    groupName: "fifth group",
//    list: [
//      { age: 22, name: "Li" },
//    ],
//  },
//]

elementFilter , . groupFilter .


import { 
  buildFilter, 
  elementField, 
  filterField, 
} from "awsome-data-filter";
import { matchText } from "awsome-data-filter/rules";

const filter = buildFilter({
    traversal: {
      getChildrenFunc: group => group.list, //    
      setChildrenFunc: (group, list) => ({ ...group, list }), //      
      getGroupsFunc: group => group.groups, //    
      setGroupsFunc: (group, groups) => ({ ...group, groups }), //    
    },
    rules: {
      elementFilter: matchText(filterField("searchText"), elementField("name")),
      groupFilter: matchText(filterField("groupName"), elementField("groupName")),
    },
  });

const filterValue = {
  searchText: "li",
  groupName: "fi",
};

const { groups } = filter(filterValue, {
  groups: dataList,
  elements: [],
});

console.log(groups);
// groups:
//[
//  {
//    groupName: "first group",
//    list: [
//      { age: 31, name: "Marina" },
//      { age: 23, name: "Fabio" },
//    ],
//  },
//  {
//    groupName: "second group",
//    groups: [
//      {
//        groupName: "third group",
//        list: [],
//        groups: [
//          {
//            groupName: "fourth group",
//            list: [{ age: 42, name: "Li" }],
//          },
//        ],
//      },
//    ],
//    list: [],
//  },
//  {
//    groupName: "fifth group",
//    list: [
//      { age: 22, name: "Li" },
//    ],
//  },
//]

first group , , , .
fifth group , , .


. :


const standardStrategy: StrategiesFilterInterface = {
  elementHandler: ({ //     
    element,
    tools: { 
        isGroupFilterIsActive, //     
        applyElementFilter //   
    },
  }) => {
    if (isGroupFilterIsActive) return null;
    if (!applyElementFilter) return element;
    return applyElementFilter(element, true) ? element : null;
  },
  groupHandler: ({
    element: group,
    originalElement: originalGroup,
    tools: {
      isGroupFilterIsActive,
      applyElementFilter,
      getIsGroupFilterHaveMatch,
      getGroupsFunc,
      getChildrenFunc,
      setChildrenFunc,
    },
  }) => {
    let newChildren = [];
    let originalChildren = [];
    const children = getChildrenFunc(group);
    const childrenExists = !!children;
    //     
    if (children) {
      originalChildren = [...children];
      newChildren = originalChildren.filter(element =>
        applyElementFilter
          ? applyElementFilter(element, !isGroupFilterIsActive)
          : !isGroupFilterIsActive,
      );
    }
    //     ,    ,   
    if (!newChildren.length && getIsGroupFilterHaveMatch(group)) {
      return originalGroup;
    }
    //     ,    
    if (childrenExists) {
      group = setChildrenFunc(group, newChildren);
    }

    //   
    const newGroups = getGroupsFunc(group);
    const isGroupsExists = !!(newGroups && newGroups.length);
    const isElementExists = !!(newChildren && newChildren.length);
    //      ,   
    return isElementExists || isGroupsExists ? group : null;
  },
};

, filterStrategy.



awesome-data-filter . .



, . .


, .


All Articles