【问题标题】:Filter array of objects with multiple AND combination attribute pairs过滤具有多个 AND 组合属性对的对象数组
【发布时间】:2021-07-07 21:23:21
【问题描述】:

我的 javascript 数组

const response = [
{
    "userId": "1",
    "questionId": "1",
    "answeredIndex": 1
},
{
    "userId": "1",
    "questionId": "2",
    "answeredIndex": 0
},
{
    "userId": "2",
    "questionId": "1",
    "answeredIndex": 1
},
{
    "userId": "2",
    "questionId": "2",
    "answeredIndex": 0
},
{
    "userId": "3",
    "questionId": "1",
    "answeredIndex": 0
},
{
    "userId": "3",
    "questionId": "2",
    "answeredIndex": 3
},
{
    "userId": "4",
    "questionId": "1",
    "answeredIndex": 1
},
{
    "userId": "4",
    "questionId": "2",
    "answeredIndex": 0
},
{
    "userId": "5",
    "questionId": "1",
    "answeredIndex": 0
},
{
    "userId": "5",
    "questionId": "2",
    "answeredIndex": 0
}]

我正在寻找一个返回用户 ID 数组的解决方案。每个用户都回答了两个问题(questionId: 1 和 questionId: 2)。我希望回答这两个问题的用户相同。所以,我想用 AND 条件过滤我的数组,如下所示:

如果用户为 questionId: 1 选择了 answersIndex: 1 并且如果同一用户也为 questionId: 2 选择了 answersIndex: 0 那么,我希望这个用户被推送到结果数组中。

我尝试了下面的代码,但不幸的是它不起作用。

const targetUsers: string[] = [];
response.forEach((feedback) => {
  if ((feedback.questionId === '1' && feedback.answeredIndex === 1) ||
       feedback.questionId === '2' && feedback.answeredIndex === 0) {
    targetUsers.push(feedback.userId);
  }
});
console.log([...new Set(targetUsers)]);

我的预期输出应该如下所示:

[
{
    "userId": "1",
    "questionId": "1",
    "answeredIndex": 1
},
{
    "userId": "1",
    "questionId": "2",
    "answeredIndex": 0
},
{
    "userId": "2",
    "questionId": "1",
    "answeredIndex": 1
},
{
    "userId": "2",
    "questionId": "2",
    "answeredIndex": 0
},
{
    "userId": "4",
    "questionId": "1",
    "answeredIndex": 1
},
{
    "userId": "4",
    "questionId": "2",
    "answeredIndex": 0
}]

因此,对于每个用户,属性对(questionId、answeredIndex)的组合应该是 (1,1) AND (2,0),然后才会考虑该用户。如果有人在这里帮助我,将不胜感激。提前致谢。

【问题讨论】:

  • 为什么有不同类型的ID?通过使用数值,您可以为所有 ID 使用数值,但不会刺激混合类型。

标签: javascript arrays object filter


【解决方案1】:

您可以对所有用户的答案采取单循环方法。然后只选择所有正确回答问题的人。

这种方法计算正确答案,然后按所需计数过滤。

const
    response = [{ userId: "1", questionId: "1", answeredIndex: 1 }, { userId: "1", questionId: "2", answeredIndex: 0 }, { userId: "2", questionId: "1", answeredIndex: 1 }, { userId: "2", questionId: "2", answeredIndex: 0 }, { userId: "3", questionId: "1", answeredIndex: 0 }, { userId: "3", questionId: "2", answeredIndex: 3 }, { userId: "4", questionId: "1", answeredIndex: 1 }, { userId: "4", questionId: "2", answeredIndex: 0 }, { userId: "5", questionId: "1", answeredIndex: 0 }, { userId: "5", questionId: "2", answeredIndex: 0 }],
    answers = { 1: 1, 2: 0 },
    temp = response.reduce((r, { userId, questionId, answeredIndex }) => {
        r[userId] ??= 0;
        r[userId] += answers[questionId] === answeredIndex;
        return r;
    }, {}),
    targetUsers = Object
        .keys(temp)
        .filter((count => k => temp[k] === count)(Object.keys(answers).length));
 
console.log(targetUsers);

【讨论】:

  • 谢谢@Nina Scholz,我接受了你的回答,简短而好...干杯...
  • 临时声明后的; 应该是, 吗? (第 8 行)
  • @BenStephens,应该。 ty
【解决方案2】:

不确定这是否是一个愚蠢的想法,但它可能允许使用复杂的过滤器:

const responses = [
  { "userId": "1", "questionId": "1", "answeredIndex": 1 },
  { "userId": "1", "questionId": "2", "answeredIndex": 0 },
  { "userId": "2", "questionId": "1", "answeredIndex": 1 },
  { "userId": "2", "questionId": "2", "answeredIndex": 0 },
  { "userId": "3", "questionId": "1", "answeredIndex": 0 },
  { "userId": "3", "questionId": "2", "answeredIndex": 3 },
  { "userId": "4", "questionId": "1", "answeredIndex": 1 },
  { "userId": "4", "questionId": "2", "answeredIndex": 0 },
  { "userId": "5", "questionId": "1", "answeredIndex": 0 },
  { "userId": "5", "questionId": "2", "answeredIndex": 0 }
];

// intersection from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
function intersection(setA, setB) {
    let _intersection = new Set()
    for (let elem of setB) {
        if (setA.has(elem)) {
            _intersection.add(elem)
        }
    }
    return _intersection
}

const matchers = [
  (item) => item.questionId === '1' && item.answeredIndex === 1,
  (item) => item.questionId === '2' && item.answeredIndex === 0,
];

const matching_users = matchers.map(
  (matcher) =>
    responses.reduce(
      (acc, response) => matcher(response) ? acc.add(response.userId) : acc,
      new Set()
    )
).reduce(
  (acc, set) => intersection(acc, set)
);

const result = responses.filter((item) => matching_users.has(item.userId));

console.log(result);

解释它(希望)做什么:

对于每个匹配器,它会检查每个响应以查看它是否匹配条件,如果匹配,则将其添加到包含用户 ID 的集合中。然后它找到用户 id 集合的公共元素以找到匹配所有匹配器的用户 id。最后,它会根据用户 ID 列表过滤响应。

【讨论】:

    【解决方案3】:

    这是个技巧,因为过滤器依赖于数组中的多个项目。解决这个问题的更容易理解的方法是保留我们已经迭代过的先前答案的信息:

    const response = [
    {
        "userId": "1",
        "questionId": "1",
        "answeredIndex": 1
    },
    {
        "userId": "1",
        "questionId": "2",
        "answeredIndex": 0
    },
    {
        "userId": "2",
        "questionId": "1",
        "answeredIndex": 1
    },
    {
        "userId": "2",
        "questionId": "2",
        "answeredIndex": 0
    },
    {
        "userId": "3",
        "questionId": "1",
        "answeredIndex": 0
    },
    {
        "userId": "3",
        "questionId": "2",
        "answeredIndex": 3
    },
    {
        "userId": "4",
        "questionId": "1",
        "answeredIndex": 1
    },
    {
        "userId": "4",
        "questionId": "2",
        "answeredIndex": 0
    },
    {
        "userId": "5",
        "questionId": "1",
        "answeredIndex": 0
    },
    {
        "userId": "5",
        "questionId": "2",
        "answeredIndex": 0
    }]
    
    
    //object to make it easier to add mulitple questions
    const rightQuestions = {
        "1": 1,
      "2": 0
    }
    
    //create a map to use as reference for each user
    const usersToKeep = new Map()
    
    response.forEach(item => {
        
      //if it's false, it has already an wrong answer
        if(usersToKeep.get(item.userId) === false) {
        return
      }
      
      // from here it either has not responded yet (undefined), or has a right answer (true)
      
        if(item.answeredIndex === rightQuestions[item.questionId]) {
        //the answer is valid
        usersToKeep.set(item.userId, true)
      } else {
            //the answer is not valid
        usersToKeep.set(item.userId, false)
      }
    })
    
    //now filter for the users we should keep
    const results = response.filter(item => usersToKeep.get(item.userId))
    
    //clear the map (not needed if the filter is inside a funcion)
    usersToKeep.clear()
    
    
    

    【讨论】:

    • 使用for..of循环,并在else块中使用break关键字,删除条件return语句
    • 使用 break 不会阻止循环迭代其他用户的答案吗?
    • 哦,我从来没想过,
    • @Nur ,continue 会按照你的想法做吗?你更喜欢for..of 而不是.forEach 的原因是什么?只是好奇。
    • .forEach 使用函数,所以性能较差,没有break 关键字,如果有10k 个项目怎么办!但只需要检查 2 个问题...然后打破循环!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-04
    • 2018-08-17
    • 1970-01-01
    • 2019-10-03
    • 2021-07-05
    • 2017-11-03
    • 2019-08-03
    相关资源
    最近更新 更多