所以我现在遇到了一些逻辑难题。这是我要解决的问题
问题
我正在读取 PDB 文件,当它遍历该文件时,它会创建文件中所有链的列表。该列表看起来像这样
chainIdList = [A, E, D, F, G, H];
长度可能会有所不同。
我有另一个序列中每个残基的所有 chainId 的列表,数据是我制作的字典,如下所示
chainResidue = {"chainId" : chainId, "residueNumber" : residueNumber}
chainResidue = { "A", "4"}
所以我想做的是迭代 chainResidues 列表并检查 chainResidue.chainId 是否在 chainList 中。如果是这样,则创建一个匹配的 chainId 的新列表,然后将所有残基编号附加到该列表中。
这有道理吗?
所以最终它看起来像
A = [ 4, 6, 7, 8, ... and so on];
E = [ 9, 10];
到目前为止的代码
for (var i = 0; i < chainResidue.length; ++i) {
for (var j = 0; j < chainList.length; ++j) {
if (chainResidue[i].chainId === chainList[j]) {
//Append value of the chainResidue[i].residueName into chainList[j] make a list of lists?
}
}
}
示例数据
ATOM 3434 CA LEU Y 17 -3.567 5.653 33.836 1.00 28.21 C
ATOM 3435 C LEU Y 17 -3.114 6.290 32.530 1.00 31.33 C
ATOM 3436 O LEU Y 17 -2.020 6.873 32.474 1.00 26.01 O
ATOM 3437 CB LEU Y 17 -2.620 4.575 34.233 1.00 29.46 C
ATOM 3438 CG LEU Y 17 -2.610 4.263 35.705 1.00 33.42 C
ATOM 3439 CD1 LEU Y 17 -1.430 3.363 35.960 1.00 40.68 C
ATOM 3440 CD2 LEU Y 17 -2.351 5.483 36.559 1.00 40.12 C
ATOM 3441 N ASP Y 18 -3.926 6.263 31.454 1.00 30.62 N
ATOM 3442 CA ASP Y 18 -3.487 6.866 30.205 1.00 31.46 C
我只是拉入“Y”和与其对应的数字,例如 17 和 18。
最佳答案
你可以使用这个 ES6 脚本:
// Sample data
var chainIdList = ['A', 'E', 'D', 'F', 'G', 'H'];
var chainResidue = [
{"chainId" : "A", "residueNumber" : 24},
{"chainId" : "E", "residueNumber" : 18},
{"chainId" : "A", "residueNumber" : 9},
{"chainId" : "A", "residueNumber" : 15}
];
// Create the empty lists to start with, per letter
var chainIdObj = chainIdList.reduce( (obj, id) => (obj[id] = [], obj), {} );
// Populate those lists with residue numbers
var result = chainResidue.reduce( (res, obj) => (res[obj.chainId] ? res[obj.chainId].push(obj.residueNumber) : 0, res), chainIdObj);
console.log(result);
代码说明
有两个主要阶段:
- 创建一个对象,该对象具有输入数组中每个字母的属性。属性值全部设置为空数组(因为我们还没有处理任何内容)。
chainIdList.reduce
迭代输入数组,并为每个元素调用为其提供的函数。该函数的第一个参数始终是前一个调用的结果。第一次,没有先前的调用,然后它从我们提供给 reduce
的第二个参数的空对象 ({}
) 开始。
传递给reduce
的函数如下所示:
(obj, id) => (obj[id] = [], obj)
这实际上是 EcmaScript6 于 2015 年引入的较新表示法。在“旧”语法中,它看起来像这样:
function (obj, id) { return obj[id] = [], obj; }
函数体使用逗号运算符,并与return
一起使用,它实际上相当于以下代码:
obj[id] = [];
return obj;
因此,综合起来,obj
的值以 {}
开头,然后在每次迭代中为其定义一个属性。第一次迭代后是
{ 'A': [] }
...并返回到reduce
内部,以便在下一次迭代中作为参数传递,等等。上一次迭代中返回的对象将作为返回值返回整个 reduce
调用。
现在我们的 chainIdObj
等于:
{
"A": [],
"E": [],
"D": [],
"F": [],
"G": [],
"H": []
}
第二阶段用于填充上述结构中的数组。再次强调,迭代是一个
reduce
;这次是chainResidue
。对chainResidue
中的每个对象执行的函数是:(res, obj) => (res[obj.chainId] ? res[obj.chainId].push(obj.residueNumber) : 0, res)
第一个参数 (res
) 的第一个值这次不是使用 {}
进行初始化,而是使用上一阶段的结果进行初始化:chainIdObj
。上面的函数检查我们正在查看的对象的 chainId
属性值是否与 res
中的条目(即 chainIdObj
)中的条目匹配。如果是这样(?
),则相应的residueNumber
将被推送到我们刚刚检查的数组中。在另一种情况下(:
)什么都不会发生。但由于三元运算符需要第三个表达式,因此我们只需输入 0
:无论如何,表达式的值都会被忽略,因此这只是一个语法填充符。
最后,再次使用逗号运算符来确保将 res
对象返回到 reduce
内部,以便我们在下一次迭代中再次获取它。最终结果是最后一次迭代的结果,由reduce
返回。它被分配给结果
。
这就是控制台中输出的内容。
功能代码
有些人喜欢尽可能避免变量赋值,并限制将它们用作函数参数。有了上面的元素,你就可以编写这样的代码:
console.log( chainResidue.reduce(
(res, obj) => (res[obj.chainId] ? res[obj.chainId].push(obj.residueNumber) : 0, res),
chainIdList.reduce( (obj, id) => (obj[id] = [], obj), {} )));
关于javascript - 将数据分组到单独的列表中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38593915/