基于 Ramda.js 的数组过滤对象

Filter object base on an array with Ramda.js

let obj = {
  tom: {
    id: 0
  },
  david: {
    id: 1
  },
  john: {
    id: 2
  }
}

let ids = [1, 2]

我想根据id过滤obj。

我想要的结果是

{ 大卫: { 编号:1 }, 约翰: { 编号:2 } }

因为上面的ids是[1, 2].

我想用 Ramda.js 来做这个。

请帮帮我。


好的,对不起。

我做了这样的事情。

let obj2 = {}
ids.forEach((x) => {
      obj2 += R.filter(y => y.id === x, obj)
      })
obj = obj2

但是,这是不正确的。

而且我不想使用 forEach。

我想用 Ramda.js 做。

您可以在纯 JavaScript 中使用 Object.entries 并使用 includes 进行解构,如下所示:

let obj = {
  tom: {
    id: 0
  },
  david: {
    id: 1
  },
  john: {
    id: 2
  }
};

let ids = [1, 2];

let result = {};

Object.entries(obj).forEach(([name, { id }]) => {
  if (ids.includes(id)) {
    result[name] = { id: id };
  }
});

console.log(result);

您只能使用 Javascript 来做到这一点,首先您可以创建一个包含您想要保留的 ids 的集合(检查集合是否有一个元素是 O(1))。然后,您可以在原始 object 上循环并在新的 object 上添加 key 和他的 value 如果该集合具有相关的 id:

let obj = {
  tom: {id: 0},
  david: {id: 1},
  john: {id: 2}
}
let ids = [1, 2];

const filterByIds = (obj, ids) =>
{
    let idsSet = new Set(ids);
    let filteredObj = {};

    for (k in obj)
    {
        if (idsSet.has(obj[k].id))
            filteredObj[k] = obj[k];
    }
    
    return filteredObj;
}

console.log(filterByIds(obj, ids));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}


更新以使用 Ramda:

使用 Ramda,您可以这样做:

let obj = {
  tom: {id: 0},
  david: {id: 1},
  john: {id: 2}
}
let ids = [1, 2];
let idsSet = new Set(ids);

const hasValidId = o => idsSet.has(o.id);
let res = R.filter(hasValidId, obj);
console.log(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

阅读文档就很容易了 here

试试这个:

let obj = { tom: { id: 0 }, david: { id: 1}, john: {id: 2} }
let ids = [1, 2] ;
let result = {} ;

for ( var o in obj ) {
  if (ids.includes(obj[o].id))
    result[o] = obj[o];
}

console.log(result) ;

一般来说,您应该努力使用对您有帮助而不是对您不利的数据结构。

使用如下所示的简化数据结构:

const obj = [
  {id: 0, name: 'tom'},
  {id: 1, name: 'david'},
  {id: 2, name: 'john'}
]

您可以使用 innerJoin:

innerJoin((cur, id) => cur.id === id, obj, [1, 2])

如何将原始数据结构转换为简化的数据结构?

使用 Ramda 可以分两步进行:

  1. 将您的对象拆分为 key/value 对数组 toPairs:

    {tom:{id:0}} ~> [['tom', {id: 0}]]
    
  2. 将每一对映射到一个对象中:

    [['tom', {id: 0}]] ~> [{name: 'tom', id: 0}]
    

const obj = {
  tom: {
    id: 0
  },
  david: {
    id: 1
  },
  john: {
    id: 2
  }
}

const convert = R.compose(R.map(([name, id]) => ({name, ...id})), R.toPairs);

console.log(convert(obj))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

除非这是学习 Ramda 的练习,否则请谨防想要在 Ramda 中执行此操作的想法。我是 Ramda 的主要作者之一,也是 Ramda 的忠实粉丝,但我需要强调的是,它只是一个工具包,在某些情况下无法提供帮助。当它有帮助时,很好,但它不应该成为目标。

现在,我确实认为它可以提供帮助。这就是我要用 Ramda 做的事情:

const matchIds = (obj, ids) => filter(propSatisfies(includes(__, ids), 'id'), obj)

let obj = {tom: {id: 0}, david: {id: 1}, john: {id: 2}}
let ids = [1, 2]

console.log(matchIds(obj, ids))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>
const {__, filter, propSatisfies, includes} = R
</script>

另一种选择,尤其是当 id 列表比对象更不可能更改时,是这样的:

const matchIds = (ids) => filter(propSatisfies(includes(__, ids), 'id'))

let obj = {tom: {id: 0}, david: {id: 1}, john: {id: 2}}
let ids = [1, 2]

console.log(matchIds(ids)(obj))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>
const {__, filter, propSatisfies, includes} = R
</script>

不喜欢占位符 ('__') 有一些原因。如果您有这种感觉,可以将 includes(__, ids) 替换为 flip(includes)(ids).

更新

我不接受我自己的建议。虽然我仍然会为 filter 使用 Ramda,但这里不需要 propSatisfies。一个简单的 lambda 就可以了:

const matchIds = (ids) => filter(({id}) => ids.includes(id))

至少在您习惯了偏应用的 Ramda 规范之后,这更加清晰易读。 (filter 有两个参数:谓词函数和要用它过滤的对象。因为我们只提供第一个,这给了我们一个期待第二个的函数。当我们用对象调用结果函数时,过滤发生了。)

我仍然会使用 Ramda 的 filter 的原因是它没有直接的内置版本应用于对象。 Ramda 提供了一种简单的替代方法来编写一次性对象过滤。