使用多个 AND 组合属性对过滤对象数组

时间:2021-04-12 19:19:33

标签: javascript arrays object filter

我的 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 条件过滤我的数组:

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

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

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),然后才会考虑该用户。如果有人在这里帮助我,我将不胜感激。提前致谢。

3 个答案:

答案 0 :(得分: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
}]


//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()


答案 1 :(得分: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);

答案 2 :(得分:1)

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

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 的集合中。然后查找user ids集合的公共元素,找到匹配所有匹配器的user ids。最后,它根据用户 ID 列表过滤响应。

相关问题