Home > Net >  Split the array into sub-arrays by multi-fields of the object
Split the array into sub-arrays by multi-fields of the object

Time:12-09

I have such array with a lot of objects:

[
  {
    startAt: "2016-01-01 11:35:00",
    fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
    toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e"
  },
  {
    startAt: "2016-01-01 11:40:00",
    fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
    toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e"
  },
  {
    startAt: "2016-01-01 12:00:00",
    fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e",
    toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
  },
  {
    startAt: "2016-01-01 12:40:00",
    fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e",
    toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
  },
  {
    startAt: "2016-01-01 11:40:00",
    fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
    toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
  },
  ...
]

I would like to split this array into sub-arrays by fields: 'fromCurrencyId', 'toCurrencyId':

[
  [
    {
      startAt: "2016-01-01 11:35:00",
      fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
      toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e"
    },
    {
      startAt: "2016-01-01 11:40:00",
      fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
      toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e"
    }
  ],
  [
    {
      startAt: "2016-01-01 12:00:00",
      fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e",
      toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
    },
    {
      startAt: "2016-01-01 12:40:00",
      fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e",
      toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
    }
  ],
  [
    {
      startAt: "2016-01-01 11:40:00",
      fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
      toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
    }
  ]
]

I've tried using lodash group by and native methods, but I don't understand how to do it correctly yet. I hope for your help.

My last attempt:

const props = ['fromCurrencyId', 'toCurrencyId']

const a = _.groupBy(data, function(note){
  return _.find(_.pick(note, props));
});

where data is my array. And next, I wanted to delete the keys from the object and thereby get my new array

CodePudding user response:

Your groupBy method should return a consistent "key" that you want to group similar objects on. Your current approach doesn't do that, as your _.find() is not being used on any data (you're only passing one argument to it).

Instead, you can do this without lodash. Below I've used reduce to create a Map (similar to an object). Each key in the map is a unique key that combines the fromCurrencyId with the toCurrencyId properties, and stores a value as an array of objects with the same currency id combination.

See working example below (and code comments for more details):

const arr = [ { startAt: "2016-01-01 11:35:00", fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a", toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e" }, { startAt: "2016-01-01 11:40:00", fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a", toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e" }, { startAt: "2016-01-01 12:00:00", fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e", toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302" }, { startAt: "2016-01-01 12:40:00", fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e", toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302" }, { startAt: "2016-01-01 11:40:00", fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a", toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302" } ];

const res = Array.from(arr.reduce((map, obj) => {
  const key = `${obj.fromCurrencyId}-${obj.toCurrencyId}`; // create the key
  const curr = map.get(key) || []; // get the grouped objects if the key already exists, otherwise creattee an empty array
  return map.set(key, curr.concat(obj)); // add the current object to the array, at our currency id key
}, new Map).values());
console.log(res);

One approach using lodash is to use _.groupBy() and then use _.at() to grab the values at fromCurrencyId and toCurrencyId (_.at() is like your _.pick(), but it returns an array of values instead of an object). This array of values is automatically converted into a string (via an implicit .toString() call) when it's used as a key for the object _.groupBy() returns. You can then take the _.values() of the object to get just the grouped values:

const arr = [ { startAt: "2016-01-01 11:35:00", fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a", toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e" }, { startAt: "2016-01-01 11:40:00", fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a", toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e" }, { startAt: "2016-01-01 12:00:00", fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e", toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302" }, { startAt: "2016-01-01 12:40:00", fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e", toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302" }, { startAt: "2016-01-01 11:40:00", fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a", toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302" } ];

const props = ['fromCurrencyId', 'toCurrencyId'];
const a = _.values(_.groupBy(arr, note => _.at(note, props), "-"));
console.log(a);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG ljU96qKRCWh quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

CodePudding user response:

here is my attend. i use some to check if the sub-arrays already exist in the result. if not, i use filter to find the matching fromCurrencyId and toCurrencyId and then create a sub-arrays and push it to the result.

const data = [
    {
        startAt: "2016-01-01 11:35:00",
        fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
        toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e"
    },
    {
        startAt: "2016-01-01 11:40:00",
        fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
        toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e"
    },
    {
        startAt: "2016-01-01 12:00:00",
        fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e",
        toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
    },
    {
        startAt: "2016-01-01 12:40:00",
        fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e",
        toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
    },
    {
        startAt: "2016-01-01 11:40:00",
        fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
        toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
    },
]

const result = data.reduce((p, c, _, arr) => {
    if (!p.some(p => p.some(p => p.fromCurrencyId === c.fromCurrencyId && p.toCurrencyId === c.toCurrencyId))) {
        p.push(arr.filter(a => a.fromCurrencyId === c.fromCurrencyId && a.toCurrencyId === c.toCurrencyId));
    }
    return p
}, [])

console.log(result);

CodePudding user response:

lodash may be able to do it in fewer steps but I wanted to demonstrate an answer without using external libraries (for now)

See the inline comments for reasoning:

// key name we're targetting
const targetIds = ["fromCurrencyId", "toCurrencyId"];

// your data
const data = [{
    startAt: "2016-01-01 11:35:00",
    fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
    toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e"
  },
  {
    startAt: "2016-01-01 11:40:00",
    fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
    toCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e"
  },
  {
    startAt: "2016-01-01 12:00:00",
    fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e",
    toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
  },
  {
    startAt: "2016-01-01 12:40:00",
    fromCurrencyId: "c5a0c46c-c5b5-49a5-9a16-4a829269583e",
    toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
  },
  {
    startAt: "2016-01-01 11:40:00",
    fromCurrencyId: "a49cd579-18ea-44cb-8117-86428b64a27a",
    toCurrencyId: "5491dfe0-7d61-407d-b642-7fd3ec905302"
  },
]

// A set of the unique values for the given key name
// `.join` is used to create a unique string made up of the values of teh targetIds
const uniq = Array.from(new Set(data.map(item => targetIds.map(target => item[target]).join(","))));
// console.log(uniq)
// Map over the Array, and then for each item
// e.g. "5491dfe0-7d61-407d-b642-7fd3ec905302"
// Filter out your original data for each item to get an Array of Arrays
// `.split` is then used in combination with `every` to find the _values_ of the targetIds
const subData = uniq.map(combinedValue => data.filter(d => combinedValue.split(",").every((value, index) => d[targetIds[index]] === value)));

console.log(subData)

Edit: I have modified my answer and rectified my mistake

  • Related