Deklarative Datenfilterung vorne. Js / ts


Müssen Sie häufig Filterhandler für Ihre Daten schreiben? Es können Arrays zum Zeichnen von Tabellen, Karten, Listen sein - alles.


Wenn die Filterung statisch ist, ist alles einfach. Standardfunktionen map, filterund reduceganz ausreichend. Was aber, wenn die Daten eine komplexe Struktur oder Verschachtelung haben und es viele Regeln für den Filter geben kann? Regeln können wiederholt werden, Daten können geändert werden und je mehr Filtersteuerelemente angezeigt werden, desto komplexer und instabiler wird der Handlercode.


Wie kann das Problem der zunehmenden Komplexität gelöst werden?


Ich selbst war mit diesem Problem konfrontiert, als ich eine Anwendung entwickelte, die mit einer großen Datenmenge arbeitet. Allmählich wurden immer mehr neue Filter hinzugefügt.


, . . , . , . - .


, .
, :


  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