ES6 / Typescript: Stacking map and filter advanced array methods

Hi everyone:

I have the following interfaces (minus details for clarity) :slight_smile:

export interface Item {
  statusOfItem: string;
}
export interface checkin {
  items: Array<Item>;
}
export interface table {
  checkins: Array<Checkin>;
}

I need to write a function that the ingoing table object, but the checkins at the table all are filtered by their status.

Here is the function I came up with:

transformTable(table: Table, status: string): Table{
  let newTable: Table = table;
  newTable.checkins = table.checkins.map(
    checkin => {
      let newCheckin: Checkin = checkin;
      newCheckin.items = checkin.items.filter(
        item => item.statusOfItem == status
      );
      return newCheckin;
    }
  )
  return newTable;
}

I feel like using the new array functions like map etc. this can be done / written even more efficiently (e.g. without the let).

Any help or feedback would be much apprecaited.

let newTable = oldTable.map(c => c.filter(i => i.statusOfItem === status));

For anyone interested:

This is the function that I ended up using:

private copyTableByStatus(table: Table, status: string): Table{
  // XXX: Weird, javascript can't deep copy. Object.assign() only creates
  // shallow copies. There should be something like Object.clone() ...
  let newTable: Table = JSON.parse(JSON.stringify(table));
  newTable.checkins = newtable.checkins.map(
    checkin => {
      checkin.items = checkin.items.filter(
        item => item.statusOItem == status
      )     
      return checkin;
    }
  )
  return newTable;
}

This shows the shortcomings of java that there is no way to deep copy stuff. Object.assign only shallow copies and this workaround doesn’t copy function of the object! :frowning:

EDIT: Edited code to be shorter and more perfomant.

Dont embed JSON.parse like that, it isn’t performant. If you need a deep copy (which you didn’t say in your question) inside a loop, you might need to write one. But this is probably a weakness in logic. You could give each item a unique ID, and then just do operations on arrays of strings, where each element of the array is an ID. Then you map from strings to the full object whenever you need to.

1 Like

Hmm apart from me not understanding your answer (sorry english is not my native language), I feel like doing this a couple of times won’t affect performance that much.

You have a master array of all things. Create a string array of all ids. You create a list of all visible things by filtering the array of ids. You then display a thing by using a Map<string, thing>.

Build a deepcopy routine in C
Cross compile to WebAssembly
Include in your project
Publish on GitHub
And become very famous

And follow aaronsterling’s advice

1 Like

I barely know C and I don’t even know what WebAssembly is :confused:

Is this something you want to achieve?

private copyTableByStatus(table: Table, status: string) {
  return {
    ...table,
    checkins: table.checkins.map(checkin => ({
      ...checkin,
      items: checkin.items.filter(item => item.statusOItem == status)
    })
    )
  };
}

The spear operator is only needed if your interfaces have some more properties.

As far as I’ concerned the spear operator only copies shallow, doesn’t it? And you are circumventing this by putting the spear in front of checkin AND table, if I understand correctly?

if table or checkin have any properties that refer to any other objects, these objects don’t get copied. You have to copy them manually. But primitive properties (string, boolean, number, etc) get copied.

Spread operator is a little bit like Object.assign. Doing shallow copies.

You could use something like this, but i don’t know, if it is better then JSON.stringify(JSON.parse(obj)) regarding performance. It is an example I just wrote for you, so it is not production ready. But there should be libraries that do similar things.

function deepcopy(obj: any) {
  if (Array.isArray(obj)) {
    return obj.map(item => deepcopy(item));
  }
  if (obj === null) {
    return obj;
  }
  if (typeof obj === 'object') {
    return Object.keys(obj).reduce((acc, curr) => ({ ...acc, [curr]: deepcopy(obj[curr]) }), {})
  }
  return obj;
}
1 Like

Thank you very much for your help!

Lodash has a deepClone method. I think that should be much better than mine.
https://lodash.com/docs/#cloneDeep

It supports all kind of features and can also clone complex structures. But it is slower compared to JSON.
The library owner describes it here:

You can install lodash to your project with:
npm i -S lodash
npm i -D @types/lodash

There is also a smaller version
npm i -S lodash.clonedeep
But i don’t know if the types are working for it.

It really depends on what you are doing. If you know exactly the data structure and it is not that big, I would use the spread operator for cloning, if performance is very important.

const obj = {
  a: {
    b: {
      c: {
        d: 'd',
        dd: 'dd'
      },
      cc: 'cc'
    },
    bb: 'bb'
  },
  aa: 'aa'
};

const objCopy = {
  ...obj,
  a: {
    ...obj.a,
    b: {
      ...obj.a.b,
      c: {
        ...obj.a.b.c,
      }
    }
  }
};