skip to Main Content

I am trying to identify duplicate object names in an Illustrator file using ExtendScript so I can group them. I can get an array of all PageItems on a layer, I just don’t know how to identify and store duplicates so I can perform the group operation.

This is a rather pathetic attempt at doing what I want:

var aLayer = app.activeDocument.activeLayer;
var pageItemsRef = aLayer.pageItems;
var duplicateNames = [];
var currentName;

for (var i = 0, len = pageItemsRef.length; i < len; i++) {
    if(i==0){
        currentName = pageItemsRef[0].name;
        continue;
    }
    if(pageItemsRef[i].name == currentName){
        var ref = {name:pageItemsRef[i].name, index:i}
        duplicateNames.push(ref);
    }
}

Continuing with this code will only give me consecutive duplicates. Sorry for the low quality example but I just don’t know where to start.

I just want to iterate pageItemsRef and store the name + ID into separate arrays.

So array [0] will have 2+ duplicates stored as objects (Name, ID)
Array [1] might have 5+ duplicates stored as objects also stored as (Name,ID)
etc.

Any more efficient approaches are warmly welcomed as well. I am new to ExtendScript.

Any help greatly appreciated.

3

Answers


  1. You can use reduce method to find duplicate,

    const duplicates = pageItemsRef.reduce((current,data,index)=>{
      current[data.name] ??= [];
      current[data.name].push({
         name:data.name,
         index
      });
      return current;
    },{});
    
    Login or Signup to reply.
  2. Try This"

    var aLayer = app.activeDocument.activeLayer;
    var pageItemsRef = aLayer.pageItems;
    var duplicates = {};
    
    for (var i = 0, len = pageItemsRef.length; i < len; i++) {
        var currentItemName = pageItemsRef[i].name;
        if (duplicates[currentItemName] == null) {
            duplicates[currentItemName] = [];
        }
        duplicates[currentItemName].push({name: currentItemName, index: i});
    }
    
    Login or Signup to reply.
  3. You can count the number of times that each targeted property value appears, then filter the array based on whether the count is greater than 1:

    TS Playground

    function findDuplicates<T, K>(
      array: readonly T[],
      selector: (element: T) => K,
    ): T[] {
      const counts = new Map<K, number>();
    
      for (const element of array) {
        const key = selector(element);
        counts.set(key, (counts.get(key) ?? 0) + 1);
      }
    
      return array.filter(element => counts.get(selector(element))! > 1);
    }
    
    /**
     * The type of a PageItem object — which apparently already has an ID:
     * - https://ai-scripting.docsforadobe.dev/jsobjref/PageItem.html#pageitem-uuid
     * - https://github.com/aenhancers/Types-for-Adobe/issues/64
     */
    type PageItem = {
      name: string;
      readonly uuid: string;
      // etc.
    };
    
    // I can't validate that the structure you presented in the question is accurate,
    // but here's a concrete example of that:
    const app = {
      activeDocument: {
        activeLayer: {
          pageItems: [
            "a", // dup
            "b",
            "c", // dup
            "a", // dup
            "c", // dup
            "d",
            "a", // dup
            "f",
          ].map(name => ({
            name,
            uuid: window.crypto.randomUUID(),
          })),
        },
      },
    } satisfies { activeDocument: { activeLayer: { pageItems: readonly PageItem[] } } };
    
    
    // Get only the duplicates like this:
    const result = findDuplicates(
      app.activeDocument.activeLayer.pageItems,
      (pageItem) => pageItem.name,
    );
    
    console.log(result); // The objects at indexes 0, 2, 3, 4, 6
    
    const expected = app.activeDocument.activeLayer.pageItems.filter(
      (_, i) => [0, 2, 3, 4, 6].includes(i),
    );
    
    console.log(JSON.stringify(result) === JSON.stringify(expected)); // true
    
    

    Compiled JS from the playground link above:

    "use strict";
    function findDuplicates(array, selector) {
        const counts = new Map();
        for (const element of array) {
            const key = selector(element);
            counts.set(key, (counts.get(key) ?? 0) + 1);
        }
        return array.filter(element => counts.get(selector(element)) > 1);
    }
    const app = {
        activeDocument: {
            activeLayer: {
                pageItems: [
                    "a",
                    "b",
                    "c",
                    "a",
                    "c",
                    "d",
                    "a",
                    "f",
                ].map(name => ({
                    name,
                    uuid: window.crypto.randomUUID(),
                })),
            },
        },
    };
    const result = findDuplicates(app.activeDocument.activeLayer.pageItems, (pageItem) => pageItem.name);
    console.log(result);
    const expected = app.activeDocument.activeLayer.pageItems.filter((_, i) => [0, 2, 3, 4, 6].includes(i));
    console.log(JSON.stringify(result) === JSON.stringify(expected));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search