skip to Main Content

This is the array what I am getting

[
  { id: 1, name: 'hello world', reference_id: null },
  { id: 2, name: 'hello world', reference_id: null },
  { id: 3, name: 'hello world', reference_id: 1 },
  { id: 4, name: 'hello world', reference_id: null },
  { id: 5, name: 'hello world', reference_id: 1 },
  { id: 6, name: 'hello world', reference_id: 2 },
]

I need to reorder this array into something similar to this.

[
  { id: 1, name: 'hello world', reference_id: null },
  { id: 3, name: 'hello world', reference_id: 1 },
  { id: 5, name: 'hello world', reference_id: 1 },
  { id: 2, name: 'hello world', reference_id: null },
  { id: 6, name: 'hello world', reference_id: 2 },
  { id: 4, name: 'hello world', reference_id: null },
]

This is the code which I tried

const parentIndex = skus?.findIndex(item => item.id === item.reference_id);
    console.log(parentIndex)

    if (parentIndex !== -1) {
      const parentId = skus[parentIndex].id;

      const parentItem = skus.splice(parentIndex, 1)[0];
      const referencedItem = skus.find(item => item.id === parentId);

      if (referencedItem) {
        const referencedIndex = skus.indexOf(referencedItem);
        skus.splice(referencedIndex + 1, 0, parentItem);
      } else {
        skus.push(parentItem);
      }
    }

When I run this code I am getting some weird unexpected results and most of the time it’s not running after the first line.

Can someone help me to resolve this issue I am struggling to find a solution.

3

Answers


  1. A .map() can help to define a sorting criterion that can be applied and then lateron removed again (by a second .map() call):

    const arr=[
      { id: 1, name: 'hello world', reference_id: null },
      { id: 2, name: 'hello world', reference_id: null },
      { id: 3, name: 'hello world', reference_id: 1 },
      { id: 4, name: 'hello world', reference_id: null },
      { id: 5, name: 'hello world', reference_id: 1 },
      { id: 6, name: 'hello world', reference_id: 2 },
    ];
    
    const res=arr.map(c=>({ref:(c.reference_id??"")+`${c.id}`,el:c}))
                 .sort((a,b)=>a.ref.localeCompare(b.ref))
                 .map(c=>c.el);
    
    console.log(res)

    OK, as requested in the comments. Here a solution in one go (just one .sort() call):

    const arr=[
      { id: 1, name: 'hello world', reference_id: null },
      { id: 2, name: 'hello world', reference_id: null },
      { id: 3, name: 'hello world', reference_id: 1 },
      { id: 4, name: 'hello world', reference_id: null },
      { id: 5, name: 'hello world', reference_id: 1 },
      { id: 6, name: 'hello world', reference_id: 2 },
    ];
    const crit=o=>(""+(o.reference_id??"")).padStart(4,"0")+"|"+(""+o.id).padStart(4,"0"),
          res=arr.sort((a,b)=>crit(a).localeCompare(crit(b)));
    
    console.log(res);
    
    console.log("these are the computed sort criteria:")
    console.log(res.map(crit))

    The second version can deal with any kid of numeric id (up to "9999").

    Login or Signup to reply.
  2. It looks like what you are after is that elements with a reference_id to immediately follow the element with that id.

    If that is the case then you are actually looking for a preorder traversal of the data as a tree (see: tree traversal). The following works for any level of nesting.

    const input = [
      { id: 4, reference_id: null },
      { id: 13, reference_id: 6 },
      { id: 5, reference_id: 1 },
      { id: 2, reference_id: null },
      { id: 1, reference_id: null },
      { id: 3, reference_id: 1 },
      { id: 6, reference_id: 2 },
    ];
    
    // build the tree
    const tree = input
      .sort((a, b) => a.id - b.id)
      .reduce((a, node) => {
        (a[node.reference_id] ??= []).push({
          node,
          children: (a[node.id] ??= []),
        });
    
        return a;
      }, {})['null'];
    
    // recursive depth-first traversal
    function flatten(arr) {
      let result = [];
      for (const { node, children } of arr) {
        result.push(node, ...flatten(children));
      }
    
      return result;
    }
    
    const sorted = flatten(tree);
    
    console.log(sorted.map((o) => JSON.stringify(o)).join(',n'));
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    If however you are just looking for a primary sorting by id with elements that share a reference_id grouped together then you can first group-by reference_id then sort by the id of the first element of each group before flattening the grouped array. Here performing an initial sort by id on a copy of the input array.

    const input = [
      { id: 4, reference_id: null },
      { id: 13, reference_id: null },
      { id: 5, reference_id: 1 },
      { id: 2, reference_id: null },
      { id: 1, reference_id: null },
      { id: 3, reference_id: 1 },
      { id: 6, reference_id: 2 },
    ];
    
    const sorted = Object
      .values(
        [...input]
          .sort((a, b) => a.id - b.id)
          .reduce((a, c, i) => ((a[c.reference_id ?? `null_${i}`] ??= []).push(c), a), {})
      )
      .sort(([a], [b]) => a.id - b.id)
      .flat();
    
    // logging
    console.log(sorted.map((o) => JSON.stringify(o)).join(',n'));
    .as-console-wrapper { max-height: 100% !important; top: 0; }
    Login or Signup to reply.
  3. You can do so by a custom sort() function:

    const list = [
      { id: 1, name: "hello world", reference_id: null },
      { id: 2, name: "hello world", reference_id: null },
      { id: 3, name: "hello world", reference_id: 1 },
      { id: 4, name: "hello world", reference_id: null },
      { id: 5, name: "hello world", reference_id: 1 },
      { id: 6, name: "hello world", reference_id: 2 }
    ];
    
    list.sort((a, b) => {
    
      // obj.reference_id = 1 goes before obj.reference_id = 2
      if (a.reference_id && b.reference_id) {
        return a.reference_id - b.reference_id;
      }
    
      // obj.reference_id = 1 goes before obj.id = 2
      if (a.reference_id && !b.reference_id) {
        return a.reference_id - b.id;
      }
    
      // again, obj.reference_id = 1 goes before obj.id = 2
      if (!a.reference_id && b.reference_id) {
        return a.id - b.reference_id;
      }
    
      // if reference_id is null then obj.id = 1 goes before obj.id = 2
      return a.id - b.id;
    });
    
    console.log(list);
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search