【问题标题】:Get every nth element of an array获取数组的每个第 n 个元素
【发布时间】:2021-10-05 20:14:30
【问题描述】:

我正在编写一个辅助函数来从数组中获取每个 nth 元素,以便根据列的索引获取特定列的所有表格单元格。

我基于this thread 的回答。

我担心的是,每当我通过列索引 0 时,它都不起作用。 我也不太确定应该将增量更改为什么,因为它不会产生正确的结果。

给定以下数组

const cells = [
  'text', 'text', 'text', 'text', 'text', 'text', 'button',
  'text', 'text', 'text', 'text', 'text', 'text', 'button',
  'text', 'text', 'text', 'text', 'text', 'text', 'button',
];

并通过getColumnCells(cells, 6) 调用该函数,我应该会收到一个“按钮”数组。

const getColumnCells = (cells, columnIndex) => {
  const columnCells = [];
  const delta = Math.floor(cells.length / columnIndex);

  for (let i = columnIndex; i < cells.length; i = i + delta) {
    columnCells.push(cells[i]);
  }

  return columnCells;
};

getColumnCells(cells, 0) 应该返回 ['text', 'text', 'text] 以获取每一行的索引 0。

【问题讨论】:

    标签: javascript arrays matrix filter reduce


    【解决方案1】:

    如果允许自己有足够的时间来获得更通用的解决方案,可以通过:nth-child() CSS pseudo-class 实现is inspiredprototypal Array.nthItem 方法,但使用基于零/0 的索引而不是1 .. .

    const cells = [
      'texta1', 'texta2', 'texta3', 'texta4', 'texta5', 'texta6', 'buttona',
      'textb1', 'textb2', 'textb3', 'textb4', 'textb5', 'textb6', 'buttonb',
      'textc1', 'textc2', 'textc3', 'textc4', 'textc5', 'textc6', 'buttonc',
    ];
    
    function processItem(nthItem, nthIndex, array) {
      console.log({ nthItem, nthIndex });
      
      // - any passed argument can be processed.
      // - this callback is aware of it's `this` context.
      // - the callback's return value will be mapped to the
      //   `nthItem`'s internal array of to be processed indices
      //   which also, entirely mapped, is the `nthItem`'s return value.
    
      return nthItem;
    }
    console.log(
      "cells.nthItem('7n-1', processItem) ...",
      cells.nthItem('7n-1', processItem)
    );
    console.log(
      "cells.nthItem('7n-5', processItem) ...",
      cells.nthItem('7n-5', processItem)
    );
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    <!--
    
      https://gist.github.com/petsel/86d7ce468512fffdb051bf9e650d1e4f
    
    //-->
    <script>
    const
      arrPrototype = Object.getPrototypeOf([]);
    
    const {
      from: arrayFrom,
      isArray,
    
    } = Array;
    
    function isFunction(type) {
      return (
           (typeof type === 'function')
        && (typeof type.call === 'function')
        && (typeof type.apply === 'function')
      );
    }
    
    function getSanitizedTarget(type) {
      return (type ?? null);
    }
    
    function getNotationGroup(notation) {
      // - inspired by `:nth-child()` CSS pseudo-class ... [https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child]
      //
      // - an array's `nth-item` notation ... [https://regex101.com/r/tZmeZS/1/]
      //
      const regXNthItemNotation = (/^(?<nthBase>[+-]?\d+)(?:\*?(?<series>n)(?<crementer>[+-]\d+)?)?$/);
      const regXSanitizeNotation = (/\s+/g);
    
      return String(notation)
        .replace(regXSanitizeNotation, '')
        .match(regXNthItemNotation)
        ?.groups;
    }
    
    function getNthIndexSeriesDescending(getNthIndex, maximumIndex) {
      const nthIndexList = [];
      let nthIndex;
      let n = -1;
      while ((nthIndex = getNthIndex(++n)) >= 0) {
        if (nthIndex <= maximumIndex) {
    
          nthIndexList.push(nthIndex);
        }
      }
      return nthIndexList;
    }
    function getNthIndexSeriesAscending(getNthIndex, maximumIndex) {
      const nthIndexList = [];
      let nthIndex;
      let n = -1;
      while ((nthIndex = getNthIndex(++n)) <= maximumIndex) {
        if (nthIndex >= 0) {
    
          nthIndexList.push(nthIndex);
        }
      }
      return nthIndexList;
    }
    
    function getNthIndexSeriesList(notationGroup, maximumIndex) {
      const nthIndexList = [];
      let { series, nthBase, crementer } = notationGroup;
    
      nthBase = Number(nthBase);
      crementer = Number(crementer ?? 0);
    
      const getNthIndex = n => ((nthBase * n) + crementer);
    
      if (!!series) {
        if (nthBase !== 0) {
    
          const isDescending = (getNthIndex(0) > getNthIndex(1));
          nthIndexList.push(
    
            ...isDescending
            ? getNthIndexSeriesDescending(getNthIndex, maximumIndex)
            : getNthIndexSeriesAscending(getNthIndex, maximumIndex)
          );
        } else if (crementer >= 0) {
    
          // `crementer` in this case equals the result of the first nth-index computation.
          nthIndexList.push(crementer);
        }
      } else if ((nthBase >= 0) && (nthBase <= maximumIndex)) {
    
        // just the `nthBase` as the sole nth-index item.
        nthIndexList.push(nthBase);
      }
      return nthIndexList;
    }
    
    function nthItem(notation, callback, target) {
      let result = [];
    
      const notationGroup = getNotationGroup(notation);
      const arr = notationGroup && ((isArray(this) && this) || arrayFrom(this ?? []));
    
      // fail silently.
      const nthIndexList = arr && isFunction(callback) && getNthIndexSeriesList(notationGroup, (arr.length - 1));
      if (nthIndexList && nthIndexList.length) {
    
        target = getSanitizedTarget(target);
        result = nthIndexList.map(nthIndex =>
    
          callback.call(target, arr[nthIndex], nthIndex, arr)
        );
      }
      return result;
    }
    
    Object.defineProperty(arrPrototype, 'nthItem', {
      configurable: true,
      writable: true,
      value: nthItem
    });
    Object.defineProperty(arrPrototype.nthItem, 'toString', {
      value: () => 'function nthItem() { [custom code] }'
    });
    
    // export default Array;
    </script>

    游乐场

    九中之七方法

    ...nth(7).of(9)...,它基于索引1...

    const cells = [
      'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9',
      'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9',
      'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9',
    ];
    
    console.log(
      "cells.nth(7).of(9).map(({ item }) => item) ...",
      cells.nth(7).of(9).map(({ item }) => item)
    );
    
    console.log(
      "cells.nth(7).of(9) ...",
      cells.nth(7).of(9)
    );
    
    console.log(
      "cells.nth(3).of(6) ...",
      cells.nth(3).of(6)
    );
    console.log(
      "cells.nth(2).of(3) ...",
      cells.nth(2).of(3)
    );
    
    console.log(
      "cells.nth ...",
      cells.nth
    );
    console.log(
      "cells.nth(2) ...",
      cells.nth(2)
    );
    console.log(
      "cells.nth(2).of ...",
      cells.nth(2).of
    );
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    <!--
    
      https://gist.github.com/petsel/42d89ad83cc5e90dc0f61a4d030fc17d
    
    //-->
    <script> 
    const
      arrPrototype = Object.getPrototypeOf([]);
    
    const {
      from: arrayFrom,
      isArray,
    
    } = Array;
    
    function createListOfChunkLists(arr, chunkCount) {
      return arr.reduce((list, item, idx) => {
        if (idx % chunkCount === 0) {
    
          list.push([ item ]);
        } else {
          // list.at(-1).push(item);
          list[list.length - 1].push(item);
        }
        return list;
      }, []);
    }
    
    function getNthItemOfChunkCount(arr, nthIndex, chunkCount) {
      if (!isArray(arr)) {
        throw (new TypeError(
          'The "nth().of()" chain needs to operate a list like structure.'
        ));
      }
      if (Number.isNaN(chunkCount)) {
        throw (new TypeError(
          'The chunk count needs to be an integer value.'
        ));
      }
      if (chunkCount > arr.length) {
        throw (new RangeError(
          "The chunk count value has to be lower than or equal to the operated list structure's length."
        ));
      }
      if (
           Number.isNaN(nthIndex)
        || (nthIndex < 0)
      //|| ((nthIndex < 0) && ((nthIndex + chunkCount) <= 0))
        || ((nthIndex >= 0) && ((nthIndex + 1) >= chunkCount))
      ) {
        throw (new RangeError(
          "The nth index needs to be an integer value which has to be reasonable within the chunk count's context."
        ));
      }
      return createListOfChunkLists(arr, chunkCount)
        .map((chunkList, idx) => ({
    
          item: chunkList[nthIndex],
          idx: ((idx * chunkCount) + nthIndex),
        }));
    }
    
    class NthItemOfChunkCount {
      #arr;
      #idx;
      constructor(arr, idx) {
        this.#arr = arr;
        this.#idx = idx;
      }
      of(int) {
        return getNthItemOfChunkCount(this.#arr, this.#idx, parseInt(int, 10));
      }
    }
    
    function nthItemOfChunkCount(int) {
      const arr = (isArray(this) && this) || arrayFrom(this ?? []);
      const idx = parseInt(int, 10) - 1;
    
      return (new NthItemOfChunkCount(arr, idx));
    }
    
    Object.defineProperty(arrPrototype, 'nth', {
      configurable: true,
      writable: true,
      value: nthItemOfChunkCount
    });
    Object.defineProperty(arrPrototype.nth, 'toString', {
      value: () => 'function nth() { [custom code] }'
    });
    
    Object.defineProperty(NthItemOfChunkCount.prototype.of, 'toString', {
      value: () => 'function of() { [custom code] }'
    });
    
    // export default Array;
    </script>

    【讨论】:

      【解决方案2】:

      我想您的目标是能够将cells 视为类似矩阵/表格的结构(基本上是2d array)。在进行任何过滤或选择之前,您可以通过将一维数组转换 为实际矩阵来模仿这种行为。这将使之后的任何工作变得容易得多。

      如果您想按照问题中描述的方式处理一维数组,恐怕除了传递某种列数量和列索引之外别无他法。

      const cells = [
        'text0', 'text1', 'text2', 'text3', 'text4', 'text5', 'button',
        'text0', 'text1', 'text2', 'text3', 'text4', 'text5', 'button',
        'text0', 'text1', 'text2', 'text3', 'text4', 'text5', 'button',
      ];
      
      const arrayToMatrix = (array, cols) =>
        Array(Math.floor(array.length / cols))
          .fill()
          .reduce((acc, _, i) => [...acc, [...array].splice(i * cols, cols)], []);
      
      const getColumnCells = (cells, cols, colIndex) =>
        arrayToMatrix(cells, cols).map((e) => e[colIndex]);
      
      console.log(getColumnCells(cells, 7, 6));
      console.log(getColumnCells(cells, 7, 0));

      我的方法的缺点是你需要非常小心你传递给getColumCell() 的参数,因为arrayToMatrix() 可能会返回一个不完整 矩阵,因此你最终会得到列值undefined.

      【讨论】:

        【解决方案3】:

        解决方案 1:您可以假设您有 7 列,并将其用作偏移量/增量。

            const cells = [
                'text', 'text', 'text', 'texts', 'text', 'text', 'button',
                'text', 'text', 'text', 'texts', 'text', 'text', 'button',
                'text', 'text', 'text', 'texts', 'text', 'text', 'button',
            ];
        
        const getColumnCells = (cells, columnIndex) => {
            const columnCells = [];
            const numberOfColumns = 7
            const delta = numberOfColumns // or equals to number of columns
            for (let i = columnIndex; i < cells.length; i = i + delta) {
                columnCells.push(cells[i]);
            }
        
            return columnCells;
        };
        console.log(getColumnCells(cells, 3)) // ['texts','texts','texts']
        console.log(getColumnCells(cells, 6)) // ['button','button','button']
        

        解决方案 2:简单一 一个 3 行 7 列的数组。所以,重塑你的数据

        const newCells = [
            ['text0', 'text1', 'text2', 'text3', 'text4', 'text', 'button'],
            ['text0', 'text1', 'text2', 'text3', 'text4', 'text', 'button'],
            ['text0', 'text1', 'text2', 'text3', 'text4', 'text', 'button'],
        ];
        const getColumnCellsTwo = (cells, columnIndex) => {
            const columnCells = [];
            for (let k = 0; k < cells.length; k++) {
                columnCells.push(cells[k][columnIndex]);
            }
        
            return columnCells;
        
        };
        
        
        console.log(getColumnCellsTwo(newCells, 0)) // ['text0','text0','text0']
        console.log(getColumnCellsTwo(newCells, 6)) // ['button','button','button']
        

        //最后的评论是:在你的代码中,当 columnIndex = 0 时,delta = Math.floor(cells.length / columnIndex) 是 Infinity。只是为了澄清。

        【讨论】:

          【解决方案4】:

          const cells = ['text1', 'text2', 'text3', 'text4', 'text5', 'text6', 'button7'];
          
          function breakInN(arr, chunk) {
            chunk--;
            return arr.filter((x, i) => i % chunk == 0 && (i+chunk != 0) || chunk == 0);
          }
          
          const output = breakInN(cells, 1);
          
          console.log({
            output
          })

          【讨论】:

          • 不应返回第一个元素。仅当提供索引 0 时。
          【解决方案5】:

          您可以使用简单的filter%(模数)操作来实现此目的。但是您需要知道 row 的长度,因为您似乎想将 1d 数组视为 2d。

          解决方案:

          const cells = [
            'texta1', 'texta2', 'texta3', 'texta4', 'texta5', 'texta6', 'buttona',
            'textb1', 'textb2', 'textb3', 'textb4', 'textb5', 'textb6', 'buttonb',
            'textc1', 'textc2', 'textc3', 'textc4', 'textc5', 'textc6', 'buttonc',
          ];
          
          const getEveryNth = (arr, rowLength, colIdx) => arr.filter(
            (_, i) => i % rowLength === colIdx
          )
          
          console.log(getEveryNth(cells, 7, 6))
          console.log(getEveryNth(cells, 7, 0))
          .as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore this */

          【讨论】:

          • columnIndex 是一个索引,意味着它可能会收到 0。
          • 你的意思是columIndexstartPoint
          • 也许它需要一些额外的信息来知道有多少列总是得到 0?如果您查看数组,它被格式化为具有行但函数不知道它。 getEveryNth(0) 应该返回 ['text,'text','text'] 以返回这 3 行中的每个 0。
          • 除非您知道rowlength,否则您无法确定。根据您的说法,您将 1d 数组视为 2d。
          • 我可以提供标题的长度。
          【解决方案6】:

          我建议在函数中再传递一个变量cols

          您可以在将来的某一天再添加一列。

          然后,您可以将cols 的不同参数传递给函数以获取输出。

          const cells = [
            'text', 'text', 'text', 'text', 'text', 'text', 'button',
            'text', 'text', 'text', 'text', 'text', 'text', 'button',
            'text', 'text', 'text', 'text', 'text', 'text', 'button',
          ];
          
          function getColumnCells(arr, cols, index) {
            const output = [];
            if (cols > index) {
              for (let i = 0; i < arr.length; i += cols) {
                const row = arr.slice(i, i + cols);
                index > row.length ? output.push(null) : output.push(row[index]);
              }
            }
            return output;
          }
          
          console.log(getColumnCells(cells, 7, 6));

          【讨论】:

            【解决方案7】:
            const cells = [
              'text', 'text', 'text', 'text', 'text', 'text', 'button',
              'text', 'text', 'text', 'text', 'text', 'text', 'button',
              'text', 'text', 'text', 'text', 'text', 'text', 'button',
            ];
            
            const getColumnCells = (cells, columnIndex) => {
              const columnCells = [];
              const delta = columnIndex + 1;
            
              // Starting at -1 (ignoring -1) so we always move by 7 and push the 7th element `button`
              for (let i = -1; i < cells.length; i = i + delta) {
                if (i != -1) {
                  columnCells.push(cells[i]);
                }
              }
            
              return columnCells;
            };
            
            console.log(getColumnCells(cells, 6));
            

            【讨论】:

              猜你喜欢
              • 2020-07-21
              • 2011-07-08
              • 1970-01-01
              • 1970-01-01
              • 2020-11-03
              • 2022-08-24
              • 2019-11-25
              • 2011-11-05
              相关资源
              最近更新 更多