Jaeeun Lee
Jaeeun Lee

Reputation: 3196

Find matching elements in multi dimensional arrays without too many nested loops?

I have an array with three levels of nesting and a one dimensional object.I need to compare the two to find matching ID's and push them into a new array as pairs. I'm just using the map method here, but perhaps there's a more efficient way to do this? I've thought of using the filter method, but I don't think it can work in this case.

THE FUNCTION:

const getMatchingIDs = function (pages, storedOBJ)  {
    const arr = []

    pages.map((page)=> {
        return page.questions.map((q)=>{
            const questionText = q.questionText

            return Object.keys(storedOBJ).map((key) => {
                const answerIndex = storedOBJ[key]

                if (typeof answerIndex !== 'undefined' && questionText === key) {
                    const answerID = q.answers[answerIndex].id
                    arr.push( q.id +  ':' + answerID)
                }
            })
        })
    })
    return arr
}

THE ARRAY AND OBJECT:

const pages = [
{
    questions: [
        {
            id: 987,
            questionText: 'Some Question',
            answers: [
                {
                    id: 154
                },
                {
                    id: 232
                },
                {
                    id: 312
                }
            ]
        },
        {
            id: 324,
            questionText: 'Another Question',
            answers: [
                {
                    id: 154
                },
                {
                    id: 232
                },
                {
                    id: 312
                }
            ]
        },
        {
            id: 467,
            questionText: 'Last Question',
            answers: [
                {
                    id: 154
                },
                {
                    id: 232
                },
                {
                    id: 312
                }
            ]
            }
        ]
    }
]

const storedOBJ = {
    'Some Question': 0,
    'Last Question': 0,
    'Another Question': 2
}

Running getMatchingIDs(pages, storedOBJ) should return ["987:154", "324:312", "467:154"]

Upvotes: 1

Views: 64

Answers (1)

ansibly
ansibly

Reputation: 398

Your use of 'map'

So for one thing, you are using the 'map' method where it would be better to use other array methods such as 'forEach' or 'reduce'. The the function passed in to the 'map' method is supposed to return an element for the new array. You are using the 'map' method just to iterate the arrays without capturing the result.

Example #1

Here is a slightly modified version of your 'matchIDs' function. The first reduce flattens the pages to make a single list of questions. The second reduce produces your matches, and skips conditions where the answer index is undefined.

function matchIDs(pages, answerMap) {
  const questions = pages.reduce((questions, page) => { return questions.concat(page.questions) }, []);

  return questions.reduce((matches, question) => {
    const answerIndex = answerMap[question.questionText];
    if(typeof answerIndex != 'undefined') matches.push(`${question.id}:${question.answers[answerIndex].id}`);
    return matches;
  }, []);
}

Example #2

In your example data you have only one page and all of your answer indexes are valid. If you can make these assumptions you could simplify further:

function matchIDs(questions, answerMap) {
  return questions.map(question => {
    const answerIndex = answerMap[question.questionText];
    return `${question.id}:${question.answers[answerIndex].id}`;
  });
}

Runnable snippet

const pages = [
  {
    questions: [
      {
        id: 987,
        questionText: 'Some Question',
        answers: [
          {
            id: 154
          },
          {
            id: 232
          },
          {
            id: 312
          }
        ]
      },
      {
        id: 324,
        questionText: 'Another Question',
        answers: [
          {
            id: 154
          },
          {
            id: 232
          },
          {
            id: 312
          }
        ]
      },
      {
        id: 467,
        questionText: 'Last Question',
        answers: [
          {
            id: 154
          },
          {
            id: 232
          },
          {
            id: 312
          }
        ]
      }
    ]
  }
];

const storedOBJ = {
  'Some Question': 0,
  'Last Question': 0,
  'Another Question': 2
};

function matchIDs(pages, answerMap) {
  const questions = pages.reduce((questions, page) => { return questions.concat(page.questions) }, []);

  return questions.reduce((matches, question) => {
    const answerIndex = answerMap[question.questionText];
    if(typeof answerIndex != 'undefined') matches.push(`${question.id}:${question.answers[answerIndex].id}`);
    return matches;
  }, []);
}

function matchIDsAlt(questions, answerMap) {
  return questions.map(question => {
    const answerIndex = answerMap[question.questionText];
    return `${question.id}:${question.answers[answerIndex].id}`;
  });
}

console.log(matchIDs(pages, storedOBJ));
console.log(matchIDsAlt(pages[0].questions, storedOBJ));

Upvotes: 2

Related Questions