skip to Main Content

I have an array of arrays like this

let a = [
  //    Line #    Vendor      Proj       Amount      Description
  [      '1',     '213e',    '2300',    '456.09',   'for gutter cleaning'],
  [      '2',     '3334',    '3321',    '321.10',   'for upkeep'],
  [      '3',     '213e',    '2300',    '456.09',   'for mowing']
]

I’d like to filter the array to only show the arrays that have the same vendor, project and amount. How can I do that in an elegant way, without using a nested loop?

I’m wondering if I can find dupes with a combination of reduce and filter? I know this isn’t in the standard, but are there any homebrewed solutions?

Something like

let dupes = a.reduceFilter((curArr, nextArr) => curArr[1] === nextArr[1] && curArr[2] === nextArr[2] && curArray[3] === nextArr[3])

And then I’d end up with this:

dupes: [
  ['1','213e','2300','456.09','for gutter cleaning'],
  ['1','213e','2300','456.09','for mowing']
]

5

Answers


  1. Chosen as BEST ANSWER

    This is not the ideal solution, but it's the best I have come up with:

    function getDupeTableData(){
      return myData.filter((dataRow,i,myData2) => {
        if(!dataRow.length)
          return false
        let pi = colIndex['PROJ']
        let vi = colIndex['VEND']
        let ai = colIndex['AMT']
        let  p = dataRow[pi]
        let  v = dataRow[vi]
        let  a = dataRow[ai]
        return myData2.filter(dr2 => {
          let p2 = dr2[pi]
          let v2 = dr2[vi]
          let a2 = dr2[ai]
          return p === p2 && v === v2 && a === a2 && dr2.length
        })
      })
    }
    

    What I'd really like to do is abstract this behind a prototype.


  2. Consider grouping the items and then filter groups bigger than one

    const cache = {}
    
    let a = [
      //    Line #    Vendor      Proj       Amount      Description
      [      '1',     '213e',    '2300',    '456.09',   'for gutter cleaning'],
      [      '2',     '3334',    '3321',    '321.10',   'for upkeep'],
      [      '3',     '213e',    '2300',    '456.09',   'for mowing']
    ]
    
    const result = a.forEach((obj) => {
      const [_, vendor, proj, amount] = obj
      const key = `${vendor}x00${proj}x00${amount}`
      if (!(key in cache)) {
        cache[key] = []
      }
      cache[key].push(obj)
    })
    
    const dupes = Object.values(cache).filter(array => array.length > 1)
    console.log(dupes)
    Login or Signup to reply.
  3. I do not ever recommend using this in production but here is my solution in a single line. Basically, use a filter to check if the rest of the items concatenated into a single string contain the vendor, project, and amount as a single string. If so, it is a dupe

    let a = [
      //    Line #    Vendor      Proj       Amount      Description
      [      '1',     '213e',    '2300',    '456.09',   'for gutter cleaning'],
      [      '2',     '3334',    '3321',    '321.10',   'for upkeep'],
      [      '3',     '213e',    '2300',    '456.09',   'for mowing']
    ]
    
    let dupes = a.filter(row => a.filter(x => x[0] !== row[0]).map(x => x.join("")).join("").includes(row.slice(1, 4).join("")))
    
    console.log(dupes)
    Login or Signup to reply.
  4. I would use reduce and flatMap to get the dupes.

    let a = [
      //    Line #    Vendor      Proj       Amount      Description
      ['1', '213e', '2300', '456.09', 'for gutter cleaning'],
      ['2', '3334', '3321', '321.10', 'for upkeep'],
      ['3', '213e', '2300', '456.09', 'for mowing']
    ]
    
    
    const findDupes = (array, keys) => {
      const mapped = array.reduce((acc, line) => {
        const genKey = keys.map(i => line[i]).join('-');
        acc[genKey] = acc[genKey] || [];
        acc[genKey].push(line);
        return acc;
      }, {});
    
      return Object.values(mapped).flatMap(items => items.length > 1 ? items : []);
    }
    
    console.log(findDupes(a, [1, 2, 3]));
    Login or Signup to reply.
  5. The code below is self-documenting.

    I broke each step of the logic out into individual functions.

    1. Reduce each row into a map (keyed-on vendor, project, and ammount)
    2. Take the values of the map and filter them by length greater than 1
    3. Flatten the resulting list
    const csvData = [
      // Line #  Vendor   Proj     Amount     Description
      [  '1',    '213e',  '2300',  '456.09',  'for gutter cleaning' ],
      [  '2',    '3334',  '3321',  '321.10',  'for upkeep'          ],
      [  '3',    '213e',  '2300',  '456.09',  'for mowing'          ]
    ];
    
    const getOrDefault = (map, key, defaultValue) => {
      if (map.has(key)) return map.get(key);
      map.set(key, defaultValue);
      return defaultValue;
    };
    
    const updateMap = (map, keyFn, row) => {
      modification(getOrDefault(map, keyFn(row), []), row);
      return map;
    };
    
    const modification = (target, value) => target.push(value);
    
    const hashFn = (...data) => data.join('x1e'); // Record Separator
    
    const findDuplicates = (data, keyFn) =>
      [...data.reduce((map, row) => updateMap(map, keyFn, row), new Map).values()]
        .filter((rows) => rows.length > 1)
        .flat();
    
    const keyFn = ([_, vendor, proj, amount]) => hashFn(vendor, proj, amount);
    
    const duplicates = findDuplicates(csvData, keyFn);
    
    console.log(duplicates);
    .as-console-wrapper { top: 0; max-height: 100% !important; }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search