Reinier68
Reinier68

Reputation: 3272

How to find indexes of nested array elements based on id?

So I have a nested array of objects that has the following structure:

let pages = [
  [
    {
      leftCol: [
        { height: 34, id: 10 },
        { height: 18, id: 20 },
        { height: 45, id: 30 },
        { height: 59, id: 40 },
      ],
    },
    {
      rightCol: [
        { height: 34, id: 50 },
        { height: 34, id: 60 },
        { height: 34, id: 70 },
      ],
    },
  ],
  [
    {
      leftCol: [
        { height: 34, id: 80 },
        { height: 18, id: 90 },
        { height: 45, id: 100 },
        { height: 59, id: 110 },
      ],
    },
    {
      rightCol: [
        { height: 34, id: 120 },
        { height: 34, id: 130 },
        { height: 34, id: 140 },
      ],
    },
  ],
];

Now I would like to create a method that can return the index of an element based on a id given as a parameter aswell as return the index of the outer element. For example:

findIdx(pages, 30) would return the idx of the element (2) aswell as the idx of the outer array (0).

findIdx(pages, 110) would return the idx of the element (3) aswell as the idx of the outer array (1).

The best I could come up with is the following method:

function getIdxAndPageNumOfColumn(array, columnIdx, payloadId) {
  let idx;
  let pageNum;

  for (pageNum = 0; pageNum < array.length; pageNum++) {
    let leftCol = array[pageNum][0].leftCol;
    let rightCol = array[pageNum][1].rightCol;
    let column = columnIdx === 0 ? leftCol : rightCol;
    idx = column.findIndex(item => item.id == payloadId);
    if (idx > -1) break;
  }

  return {
    idx: idx,
    pageNum: pageNum,
  };
}

But this requires me to specify a columnIdx as a parameter (a columnIdx of 0 will always be equal to the leftCol in the pages array, and columnIdx of 1 would be equal to rightCol). For some reason I can't get this to work properly. Any help would be appreciated.

Upvotes: 0

Views: 3993

Answers (4)

Nina Scholz
Nina Scholz

Reputation: 386728

You could take a dynamic approach for getting all indices of the wanted object.

function findIdx(array, id) {
    function find(array) {
        if (!array) return;
        let inner,
            index = array.findIndex(o => {
                if (o.id === id) return true;
                if (Array.isArray(o)) return inner = find(o);
                return inner = find(o.leftCol || o.rightCol);
            });
        return index !== -1 && [index, ...(inner || [])];
    }

    return find(array);
}

let pages = [[{ leftCol: [{ height: 34, id: 10 }, { height: 18, id: 20 }, { height: 45, id: 30 }, { height: 59, id: 40 }] }, { rightCol: [{ height: 34, id: 50 }, { height: 34, id: 60 }, { height: 34, id: 70 }] }], [{ leftCol: [{ height: 34, id: 80 }, { height: 18, id: 90 }, { height: 45, id: 100 },{ height: 59, id: 110 }] }, { rightCol: [{ height: 34, id: 120 }, { height: 34, id: 130 }, { height: 34, id: 140 }] }]];

console.log(findIdx(pages, 30));  // [0, 0, 2]
console.log(findIdx(pages, 110)); // [1, 0, 3]

Upvotes: 2

pilchard
pilchard

Reputation: 12918

A reducer makes for a fairly concise and readable solution, though in the example below it requires the structure to remain static as it references leftCol and rightCol specifically. You could make it more general by replacing the desctructuring with an Object.entries() loop.

Directly destructuring the page object

let pages = [ [ { leftCol: [ { height: 34, id: 10 }, { height: 18, id: 20 }, { height: 45, id: 30 }, { height: 59, id: 40 }, ], }, { rightCol: [ { height: 34, id: 50 }, { height: 34, id: 60 }, { height: 34, id: 70 }, ], }, ], [ { leftCol: [ { height: 34, id: 80 }, { height: 18, id: 90 }, { height: 45, id: 100 }, { height: 59, id: 110 }, ], }, { rightCol: [ { height: 34, id: 120 }, { height: 34, id: 130 }, { height: 34, id: 140 }, ], }, ],];

function getIdxAndPageNumOfColumn(array, payloadId) {

  const pageAndIndex = array.reduce((acc, [{leftCol}, {rightCol}], i) =>  {
    if ((leftMatch = leftCol.findIndex(p => p.id === payloadId)) !== -1) {
      acc['idx'] = leftMatch;
      acc['pageNum'] = i;
      acc['colIdx'] = 0;
    } else if ((rightMatch = rightCol.findIndex(p => p.id === payloadId)) !== -1) {
      acc[idx] = rightMatch;
      acc['pageNum'] = i;
      acc['colIdx'] = 1;
    }
    return acc
  },{})
  
  return pageAndIndex;
}

console.log(getIdxAndPageNumOfColumn(pages, 30));
console.log(getIdxAndPageNumOfColumn(pages, 110));


Using Object.values

let pages = [[{ leftCol: [{ height: 34, id: 10 }, { height: 18, id: 20 }, { height: 45, id: 30 }, { height: 59, id: 40 },], }, { rightCol: [{ height: 34, id: 50 }, { height: 34, id: 60 }, { height: 34, id: 70 },], },], [{ leftCol: [{ height: 34, id: 80 }, { height: 18, id: 90 }, { height: 45, id: 100 }, { height: 59, id: 110 },], }, { rightCol: [{ height: 34, id: 120 }, { height: 34, id: 130 }, { height: 34, id: 140 },], },],];

function getIdxAndPageNumOfColumn(array, payloadId) {

  const pageAndIndex = array.reduce((acc, pArr, pi) => {
    pArr.forEach((col, ci) => {
      Object.values(col).forEach(v => {
        if ((match = v.findIndex(p => p.id === payloadId)) !== -1) {
          acc['idx'] = match;
          acc['pageNum'] = pi;
          acc['colIdx'] = ci;
        }
      });
    });
    return acc
  }, {});
  
  return pageAndIndex;
}

console.log(getIdxAndPageNumOfColumn(pages, 30));
console.log(getIdxAndPageNumOfColumn(pages, 110));

Upvotes: 1

Hasip Timurtas
Hasip Timurtas

Reputation: 980

Here you are.

function findIdx(pages, id) {
    for (let index = 0; index < pages.length; index++) {
        const cols = pages[index];
        for (const e of cols) {
            for (const key in e) {
                const item = e[key].find((a) => a.id == id);
                if (item) return index;
            }
        }
    }
}

const item = findIdx(pages, 110);
console.log(item);

Upvotes: 1

Veyis Aliyev
Veyis Aliyev

Reputation: 323

Try:

const findPage = (pages, pageid) =>
  new Promise((res) => {
    pages.map((page) => {
      page.map((p) => {
        Object.entries(p).map(([k, v]) => {
          const x = v.filter((_) => _.id === pageid);
          if (x.length > 0) {
            res(x["0"]);
          }
        });
      });
    });
  });

findPage(pages, idWhichYouSearch).then(console.log);

Upvotes: 1

Related Questions