ey dee ey em
ey dee ey em

Reputation: 8603

what are the ways to write an algorithm that add a property to each object of a nested array in javascript?

    const data = [
      1,2,3,4,5,[6,7,8],
    ]

    function goEach(items) {
      if (items.length) {
        items.forEach((item) => goEach(item))
      } else {
        console.log(items);
      }
    }

    let newList = [];
    function buildNewList(items) {
      if (items.length) {
        items.forEach((item) => buildNewList(item))
      } else {
        newList.push(items);
        // console.log(items);
      }
    }

    let simList = [];
    function buildSimList(items) {
      if (items.length) {
        items.forEach((item) => buildSimList(item))
      } else {
        simList.push({id: items, checked: false});
        // console.log(items);
      }
    }

buildSimList is what I am working on. I know how to traverse each item as well as flattening a nested array with recursion. However, my brain getting stucked on the best way to produce an array with identical array structure but in different data format. For example, my buildSimList should be converting the data array into an array similar to this

   [
      {id: 1, clicked: false},
      {id: 2, clicked: false},
      {id: 3, clicked: false},
      {id: 4, clicked: false},
      {id: 5, clicked: false},
      [
        {id: 6, clicked: flase},
        {id: 7, clicked: flase},
        {id: 8, clicked: flase},
      ],
    ]

Is there an intuitive way to achieve it? I can't think of any. even using a library like lodash can be a good solution as well.

Upvotes: 1

Views: 84

Answers (6)

Mulan
Mulan

Reputation: 135227

I would recommend a generic function like Array.prototype.map but one the maps nested arrays too – we'll call it deepMap.

const identity = x =>
  x

const deepMap = (f = identity, xs = []) =>
  xs.map (x =>
    Array.isArray (x)
      ? deepMap (f, x)
      : f (x))
      
const makeObject = (id = 0, clicked = false) =>
  ({ id, clicked })

const data =
  [ 1, 2, 3, 4, 5, [ 6, 7, 8 ] ]

console.log (deepMap (makeObject, data))
// [ { id : 1, clicked : false }
// , { id : 2, clicked : false }
// , { id : 3, clicked : false }
// , { id : 4, clicked : false }
// , { id : 5, clicked : false }
// , [ { id : 6, clicked : false }
//   , { id : 7, clicked : false }
//   , { id : 8, clicked : false }
//   ]
// ]

We get a better picture of how this works if we first understand map, which only performs a shallow transformation – Below, we reimplement deepMap but this time using our own implementation of map

const identity = x =>
  x

const None =
  Symbol ()

const map = (f = identity, [ x = None, ...xs ] = []) =>
  x === None
    ? []
    : [ f (x) , ...map (f, xs) ]

const deepMap = (f = identity, xs = []) =>
  map (x =>
    Array.isArray (x)
      ? deepMap (f, x)
      : f (x), xs)
      
const makeObject = (id = 0, clicked = false) =>
  ({ id, clicked })

const data =
  [ 1, 2, 3, 4, 5, [ 6, 7, 8 ] ]

console.log (deepMap (makeObject, data))
// [ { id : 1, clicked : false }
// , { id : 2, clicked : false }
// , { id : 3, clicked : false }
// , { id : 4, clicked : false }
// , { id : 5, clicked : false }
// , [ { id : 6, clicked : false }
//   , { id : 7, clicked : false }
//   , { id : 8, clicked : false }
//   ]
// ]

Upvotes: 0

Dan Karbayev
Dan Karbayev

Reputation: 2920

When you want to transform an array to another array, the first thing to think of is map. Given your requirements, here's my solution to your problem:

const data = [
  1, 2, 3, 4, 5, [6, 7, 8]
];

const buildSimList = (array) =>
  // transform array with map
  array.map(item =>
    // check if current item is array
    item instanceof Array
      // if it is an array then recursively transform it
      ? buildSimList(item)
      // if not, then convert the number to an object
      : { id: item, checked: false }
  );

console.log(buildSimList(data));

Upvotes: 1

JJWesterkamp
JJWesterkamp

Reputation: 7916

Explanation uses symbols from my code snippet


When iterating data within convert we'll have to cover for 2 cases:

  • numOrArray is a number, in which case we map it toObject
  • numOrArray is an array, in which case we start a nested iteration. This may be a recursive call, since the logic is identical for nested arrays.

// Converts given number to an object with id and clicked properties:

const toObject = (num) => ({ id: num, clicked: false });


// Iterates given data and converts all numbers within to
// objects with toObject. If an array is encountered,
// convert is called recursively with that array as argument.

const convert = (data) => {
    return data.map((numOrArray) => Array.isArray(numOrArray)
        ? convert(numOrArray)
        : toObject(numOrArray));
}


// Test drive!

const data = [1, 2, 3, 4, 5, [6, 7, 8]];
console.log(convert(data));

Upvotes: 2

Sylwester
Sylwester

Reputation: 48745

You were close since you do one object for non arrays and recurse when not, but you are adding to the same array and thus that will result in a flattened structure. Here is the same where each array become an array and each element become an object using higher order functions:

const data = [1,2,3,4,5,[6,7,8]]

function convert(e) {
  return e instanceof Array ? 
    e.map(convert) :
    { id: e, clicked: false };
}

console.log(convert(data));

Also know that using the function twice won't give you the same oddity that you get the earlier result combined as in your code. The reson for that is that I do not use a global accumulator. Should I use an accumulator I would do something like this:

function convert(e) {
  function helper(e, acc) {
    ...
  }
  return helper(e, []);
}

or if you really want mutation:

function convert(e) {
  let acc = [];
  function helper(e) {
    acc.push(...);
  }
  helper(e);
  return acc;
}

Upvotes: 2

Ele
Ele

Reputation: 33726

Use forEach function along with recursion and check for the element's type using either typeof keyword or instanceof keyword.

const data = [1, 2, 3, 4, 5, [6, 7, 8]];

let convert = (acc /*This is the current accumulator*/ , array) => {
  array.forEach((n) => {
    if (n instanceof Array) {
      let newAcc = []; // New accumulator because the iteration found an Array.
      acc.push(newAcc);
      convert(newAcc, n); // Recursion for the new accumulator
    } else acc.push({ id: n, clicked: false });
  });
};

let result = [];
convert(result, data);
console.log(result);
.as-console-wrapper {
  max-height: 100% !important
}

Upvotes: 1

CRice
CRice

Reputation: 32176

Both converting items in a nested array, and flattening such an array are pretty simple if you use a recursive function.

All you have to do go through each item, and if it's not another array, perform the transformation you want. Otherwise, recurse. Here's an example for the data in your snippet:

const data = [1,2,3,4,5,[6,7,8]]

function convert(arr) {
    return arr.map(item => {
        if (item instanceof Array) return convert(item);
        return {id: item, clicked: false};
    });
}

const mapped = convert(data);
console.log(mapped);

function flatten(arr) {
    return [].concat(...arr.map(item => {
        if (item instanceof Array) return flatten(item);
        return [item];
    }));
}

const flat = flatten(mapped);
console.log(flat)

Upvotes: 1

Related Questions