KnockoutJS 按特定属性过滤

Posted

技术标签:

【中文标题】KnockoutJS 按特定属性过滤【英文标题】:KnockoutJS filter by specific property 【发布时间】:2019-05-30 03:59:36 【问题描述】:

我将 KnockoutJS 用于在 JS 中使用 getJSON 方法的求职网站。

不幸的是,我得到了这个结构:

办公室

纽约

部门 财务 工作 示例 ... 它 物流 营销

华盛顿

部门 财务 它 物流 营销

洛杉矶

部门 财务 它 物流 营销

我用JS中的filter-函数过滤掉了一些尚未开放的城市办公室,效果很好。

但现在我需要过滤掉所有部门除了物流,因为我只想显示特定城市的物流工作。我希望它是动态的,因此即使有更多部门来,它也只会显示物流

我找不到一个好的解决方案。有什么想法吗?

编辑:这是虚拟 JSON:

【问题讨论】:

您是否尝试过创建处理程序?你能用虚拟 JSON 分享你的代码吗? 我尝试了很多东西,但问题似乎是深层嵌套的 JSON 结构。如果filter-function 没有嵌套那么深,它就可以工作。稍后我将分享我的代码。 @AmitBhoyar 现在您可以看到 JSON 结构了。 1) 请创建一个minimal reproducible example。您的代码/json 应该在问题和not an image of it 中。我们无法复制图像来创建答案。 2)输入和输出是什么?输入是部门名称和城市名称,输出是工作列表?请为您的代码创建一个最小的 sn-p 或 jsfiddle。 【参考方案1】:

由于您对工作感兴趣,我建议您制作一个Job 模型,将当前仅按结构定义的数据合并到一个方便的对象中。

为了展平您的数据,您需要执行一组reduce 操作:

const jobData=offices:[location:"ny",departments:[name:"Logistics",jobs:[title:"driver for x",title:"driver for y"],name:"Finance",jobs:[title:"CFO"]],location:"la",departments:[name:"Logistics",jobs:[title:"driver for z"],name:"IT",jobs:[title:"tech support manager"]]]


const Job = (title, department, officeLocation) => (
  title,
  department,
  officeLocation
);

const JobList = ( offices ) => (
  jobs: offices.reduce(
    (allJobs,  location, departments ) => departments.reduce(
      (allJobs,  name, jobs ) => jobs.reduce(
        (allJobs,  title ) => allJobs.concat(
          Job(title, name, location)
        ),
        allJobs
      ),
      allJobs
    ),
    []
  )
)

console.log(JobList(jobData))

现在我们已经整理好了数据格式,我们可以开始编写淘汰赛代码了。

我创建了一个table,它呈现一个计算出的作业列表。在计算中,我们过滤 2 个属性:所需的办公室和所需的部门。

过滤器本身是“扁平的”,因为Job 对象包含我们需要的所有数据。例如,物流过滤器可以这样应用:

const logisticsJobs = ko.pureComputed(
  jobList().filter(job => job.department === "logistics")
);

这是一个例子。使用表头中的<select> 元素来应用过滤器。

function JobFinder() 
  const jobData = ko.observable( offices: [] );
  const jobList = ko.pureComputed(
    () => JobList(jobData())
  );
  
  // Lists of properties we can filter on
  this.offices = ko.pureComputed(
    () => uniques(jobList().map(job => job.officeLocation))
  );
  
  this.departments = ko.pureComputed(
    () => uniques(jobList().map(job => job.department))
  );
  
  // Filter values
  this.requiredOffice = ko.observable(null);
  this.requiredDepartment = ko.observable(null);
  
  // Actual filter logic
  const officeFilter = ko.pureComputed(
    () => this.requiredOffice()
      ? job => job.officeLocation === this.requiredOffice()
      : () => true
  );
  
  const departmentFilter = ko.pureComputed(
    () => this.requiredDepartment()
      ? job => job.department === this.requiredDepartment()
      : () => true
  );
  
  const allFilters = ko.pureComputed(
    () => [ officeFilter(), departmentFilter() ]
  )
  
  const filterFn = ko.pureComputed(
    () => job => allFilters().every(f => f(job))
  )
  
  // The resulting list
  this.filteredJobs = ko.pureComputed(
    () => jobList().filter(filterFn())
  );

  // To load the data (can be async in real app)
  this.loadJobData = function() 
    jobData(getJobData());
  
;

// Initialize app
const app = new JobFinder();
ko.applyBindings(app);
app.loadJobData();


// utils
function uniques(xs)  return Array.from(new Set(xs)); 


// Code writen in the previous snippet:
function getJobData()  
  return offices:[location:"ny",departments:[name:"Logistics",jobs:[title:"driver for x",title:"driver for y"],name:"Finance",jobs:[title:"CFO"]],location:"la",departments:[name:"Logistics",jobs:[title:"driver for z"],name:"IT",jobs:[title:"tech support manager"]]];
;


function Job(title, department, officeLocation) 
  return 
    title,
    department,
    officeLocation
  
;

function JobList( offices ) 
  return offices.reduce(
    (allJobs,  location, departments ) => departments.reduce(
      (allJobs,  name, jobs ) => jobs.reduce(
        (allJobs,  title ) => allJobs.concat(
          Job(title, name, location)
        ),
        allJobs
      ),
      allJobs
    ),
    []
  )
;
th  
  text-align: left;
  width: 30% 
;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<table>
  <thead>
    <tr>
      <th>Job Title</th>
      <th>Location</th>
      <th>Department</th>
    </tr>
    <tr>
      <th></th>
      <th>
        <select data-bind="
          options: offices,
          value: requiredOffice,
          optionsCaption: 'Show all locations'">
        </select>
      </th>
        <th>
        <select data-bind="
          options: departments,
          value: requiredDepartment,
          optionsCaption: 'Show all departments'">
        </select>
      </th>
    </tr>
  </thead>
  <tbody data-bind="foreach: filteredJobs">
    <tr>
      <td data-bind="text: title"></td>
      <td data-bind="text: officeLocation"></td>
      <td data-bind="text: department"></td>
    </tr>
  </tbody>
</table>

【讨论】:

非常感谢您提供如此详细的回答。我会试试看。

以上是关于KnockoutJS 按特定属性过滤的主要内容,如果未能解决你的问题,请参考以下文章

按 BETWEEN 属性过滤核心数据结果

按属性过滤对象并使用 jmespath 中的键进行选择

NSPredicate 按属性过滤核心数据关系 NSSet

针对特定数据字段的高效搜索算法

使用knockoutjs在文本框“keydown”之后过滤的显示列表

BreezeJS 的二进制谓词问题的左侧