Сортировать массив объектов и взять N элементов - PullRequest
0 голосов
/ 06 ноября 2018

в сервисе NodeJS У меня есть массив с объектами, которые имеют эти свойства:

  • batchType: string
  • batchId: строка (это хеш)
  • actionId: строка (это хеш)

В этом массиве хранятся все транзакции разных типов пакетов.

По сути, мне нужно получить N элементов из массива, но соблюдая определенные правила:

  1. По крайней мере, получить 1 предмет из каждого типа партии
  2. По крайней мере, получить 1 элемент для каждого идентификатора партии
  3. Иногда массив может иметь только один тип пакета

Это пример массива:

let batchTransactionsArray = [
  { batchType: 'type1', batchId: '123', transactionId: 'ffasf23' },
  { batchType: 'type1', batchId: '312', transactionId: '423' },
  { batchType: 'type1', batchId: '123', transactionId: '534' },
  { batchType: 'type1', batchId: '312', transactionId: '86' },
  { batchType: 'type2', batchId: '111', transactionId: '97' },
  { batchType: 'type1', batchId: '312', transactionId: '1945' },
  { batchType: 'type1', batchId: '123', transactionId: '79' },
  { batchType: 'type1', batchId: '312', transactionId: '79' },
  { batchType: 'type3', batchId: '425', transactionId: '1555645' },
  { batchType: 'type1', batchId: '123', transactionId: 'fg5' },
  { batchType: 'type1', batchId: '123', transactionId: 'jkh5' },
  { batchType: 'type1', batchId: '312', transactionId: '53j' },
  { batchType: 'type1', batchId: '111', transactionId: '4545' },
  { batchType: 'type2', batchId: '111', transactionId: '534l' },
  { batchType: 'type1', batchId: '111', transactionId: 'jkg435' },
  { batchType: 'type1', batchId: '111', transactionId: 'gfxg23' },
  { batchType: 'type1', batchId: '111', transactionId: '7asdt' },
  { batchType: 'type1', batchId: '222', transactionId: 'jdsa7' },
  { batchType: 'type3', batchId: '663', transactionId: '12423445' },
  { batchType: 'type1', batchId: '111', transactionId: '89saf6' },
  { batchType: 'type1', batchId: '111', transactionId: '12h3g' },
  { batchType: 'type1', batchId: '111', transactionId: '4h3k2hj' },
  { batchType: 'type3', batchId: '663', transactionId: '145' }
];

И пример вывода мне нужен (если я хочу 5 транзакций из массива):

[{ batchType: 'type1', batchId: '123', transactionId: '534' },
 { batchType: 'type1', batchId: '312', transactionId: '86' },
 { batchType: 'type2', batchId: '111', transactionId: '97' },
 { batchType: 'type2', batchId: '111', transactionId: '534l' },
 { batchType: 'type3', batchId: '663', transactionId: '145' }
]

Критерии для сортировки идентификаторов транзакций были бы случайными, особого порядка не существует.

Я пробовал некоторые функции lodash, такие как groupBy и sortBy, но пока не повезло.

Вот jsfiddle, где я играл с этим: https://jsfiddle.net/20jh3ze7/

Я действительно ценю предложения.

Ответы [ 3 ]

0 голосов
/ 06 ноября 2018

Вы можете сделать это шагами:

  • сначала добавить уникальные элементы batchType (это соответствует первому критерию)
  • затем добавьте уникальные элементы batchId (это соответствует второму критерию)
  • заполнить массив оставшимися элементами, если осталось еще немного места (чтобы перейти к N элементам)

Чтобы проверить уникальность, вы можете использовать Set. Поскольку вы сказали, что используете lodash, вы можете использовать _.groupBy для группировки массива по batchItem и batchId для получения уникальных элементов из каждой группы.

let batchTransactionsArray = [
  { batchType: 'type1', batchId: '123', transactionId: 'ffasf23' },
  { batchType: 'type1', batchId: '312', transactionId: '423' },
  { batchType: 'type1', batchId: '123', transactionId: '534' },
  { batchType: 'type1', batchId: '312', transactionId: '86' },
  { batchType: 'type2', batchId: '111', transactionId: '97' },
  { batchType: 'type1', batchId: '312', transactionId: '1945' },
  { batchType: 'type1', batchId: '123', transactionId: '79' },
  { batchType: 'type1', batchId: '312', transactionId: '79' },
  { batchType: 'type3', batchId: '425', transactionId: '1555645' },
  { batchType: 'type1', batchId: '123', transactionId: 'fg5' },
  { batchType: 'type1', batchId: '123', transactionId: 'jkh5' },
  { batchType: 'type1', batchId: '312', transactionId: '53j' },
  { batchType: 'type1', batchId: '111', transactionId: '4545' },
  { batchType: 'type2', batchId: '111', transactionId: '534l' },
  { batchType: 'type1', batchId: '111', transactionId: 'jkg435' },
  { batchType: 'type1', batchId: '111', transactionId: 'gfxg23' },
  { batchType: 'type1', batchId: '111', transactionId: '7asdt' },
  { batchType: 'type1', batchId: '222', transactionId: 'jdsa7' },
  { batchType: 'type3', batchId: '663', transactionId: '12423445' },
  { batchType: 'type1', batchId: '111', transactionId: '89saf6' },
  { batchType: 'type1', batchId: '111', transactionId: '12h3g' },
  { batchType: 'type1', batchId: '111', transactionId: '4h3k2hj' },
  { batchType: 'type3', batchId: '663', transactionId: '145' }
];

function getNItems(array, N = 5) {
  if (N >= array.length) return array;
  let batchTypeGroups = _.groupBy(array, e => e.batchType),
      res  = new Set(), batchIds = new Set();
  
  // add unique batchTypes
  for (let [batchType, elements] of Object.entries(batchTypeGroups)) {
    if (res.size === N) break;
    let d = elements.find(e => !batchIds.has(e.batchId));
    if (d) {
      res.add(d);
      batchIds.add(d.batchId);
    } else {
      res.add(elements[0]);
    }
  }
  
  // add remaining unique batchIds
  let batchIdGroups = _.groupBy(array.filter(e => !batchIds.has(e.batchId)), e => e.batchId);
  for (let [batchId, elements] of Object.entries(batchIdGroups)) {
    if (res.size === N) break;
    res.add(elements[0]);
  }
  
  // add any remaining elements until we have N elements
  for (let e of array) {
    if (res.size === N) break;
    res.add(e);
  }
  
  return Array.from(res);
}

console.log(getNItems(batchTransactionsArray, 5));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
0 голосов
/ 07 ноября 2018

Я думаю, что для этого типа проблемы нам не нужны дополнительные зависимости.

Мы можем сделать это простым javascript:)

let batchTransactionsArray = [
  { batchType: "type1", batchId: "123", transactionId: "ffasf23" },
  { batchType: "type1", batchId: "312", transactionId: "423" },
  { batchType: "type1", batchId: "123", transactionId: "534" },
  { batchType: "type1", batchId: "312", transactionId: "86" },
  { batchType: "type2", batchId: "111", transactionId: "97" },
  { batchType: "type1", batchId: "312", transactionId: "1945" },
  { batchType: "type1", batchId: "123", transactionId: "79" },
  { batchType: "type1", batchId: "312", transactionId: "79" },
  { batchType: "type3", batchId: "425", transactionId: "1555645" },
  { batchType: "type1", batchId: "123", transactionId: "fg5" },
  { batchType: "type1", batchId: "123", transactionId: "jkh5" },
  { batchType: "type1", batchId: "312", transactionId: "53j" },
  { batchType: "type1", batchId: "111", transactionId: "4545" },
  { batchType: "type2", batchId: "111", transactionId: "534l" },
  { batchType: "type1", batchId: "111", transactionId: "jkg435" },
  { batchType: "type1", batchId: "111", transactionId: "gfxg23" },
  { batchType: "type1", batchId: "111", transactionId: "7asdt" },
  { batchType: "type1", batchId: "222", transactionId: "jdsa7" },
  { batchType: "type3", batchId: "663", transactionId: "12423445" },
  { batchType: "type1", batchId: "111", transactionId: "89saf6" },
  { batchType: "type1", batchId: "111", transactionId: "12h3g" },
  { batchType: "type1", batchId: "111", transactionId: "4h3k2hj" },
  { batchType: "type3", batchId: "663", transactionId: "145" }
];

const getUniqueBatches = () => {
  const uniqueBatches = [];

  batchTransactionsArray.forEach(b => {
    const batchByType = uniqueBatches.find(findBatchByType(b.batchType));
    const batchById = uniqueBatches.find(findBatchByBatchId(b.batchId));
    if (!batchByType || !batchById) {
      uniqueBatches.push(b);
    }
  });

  return uniqueBatches;
};

const findBatchByType = batchType => {
  return batch => {
    return batch.batchType === batchType;
  };
};
const findBatchByBatchId = batchId => {
  return batch => {
    return batch.batchId === batchId;
  };
};

console.log(getUniqueBatches());
0 голосов
/ 06 ноября 2018

Вы можете сделать что-то подобное, используя lodash:

let data = [ { batchType: 'type1', batchId: '123', transactionId: 'ffasf23' }, { batchType: 'type1', batchId: '312', transactionId: '423' }, { batchType: 'type1', batchId: '123', transactionId: '534' }, { batchType: 'type1', batchId: '312', transactionId: '86' }, { batchType: 'type2', batchId: '111', transactionId: '97' }, { batchType: 'type1', batchId: '312', transactionId: '1945' }, { batchType: 'type1', batchId: '123', transactionId: '79' }, { batchType: 'type1', batchId: '312', transactionId: '79' }, { batchType: 'type3', batchId: '425', transactionId: '1555645' }, { batchType: 'type1', batchId: '123', transactionId: 'fg5' }, { batchType: 'type1', batchId: '123', transactionId: 'jkh5' }, { batchType: 'type1', batchId: '312', transactionId: '53j' }, { batchType: 'type1', batchId: '111', transactionId: '4545' }, { batchType: 'type2', batchId: '111', transactionId: '534l' }, { batchType: 'type1', batchId: '111', transactionId: 'jkg435' }, { batchType: 'type1', batchId: '111', transactionId: 'gfxg23' }, { batchType: 'type1', batchId: '111', transactionId: '7asdt' }, { batchType: 'type1', batchId: '222', transactionId: 'jdsa7' }, { batchType: 'type3', batchId: '663', transactionId: '12423445' }, { batchType: 'type1', batchId: '111', transactionId: '89saf6' }, { batchType: 'type1', batchId: '111', transactionId: '12h3g' }, { batchType: 'type1', batchId: '111', transactionId: '4h3k2hj' }, { batchType: 'type3', batchId: '663', transactionId: '145' } ];

const customTake = (d, n) => {
  const roundRobinUnion = (arr) => {
    let res = []
    while (_.flatten(arr).length)
      _.each(arr, x => x.length ? res.push(_.remove(x, (y, i) => i == 0)) : null)
    return _.flatten(res)
  }
  const groups = _(d)
    .orderBy(['batchType', 'batchId'])
    .groupBy('batchType')
    .mapValues(x => _.values(_.groupBy(x, 'batchId')))
    .map(x => roundRobinUnion(x))
    .value()
  return _.take(roundRobinUnion(groups), n)
}

console.log(customTake(data, 3))
console.log(customTake(data, 5))
console.log(customTake(data, 6))
console.log(customTake(data, 8))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Идея состоит в том, чтобы сгруппировать по batchType и batchId и думать об этой проблеме как о круглом объединении. Вы просматриваете каждый индекс массива и делаете объединение каждого элемента.

Если вам нужен конечный порядок сортировки, вы всегда можете сделать еще один orderBy в конце и т. Д.

Вот простой пример идеи roundRobinUnion:

const data = [
  [1, 2, 3],
  [1],
  [5, 6]
]

const roundRobinUnion = (arr) => {
  let res = []
  while (_.flatten(arr).length)
    _.each(arr, x => x.length ? res.push(_.remove(x, (y, i) => i == 0)) : null)
  return _.flatten(res)
}

console.log(roundRobinUnion(data))  // [1,1,5,2,6,3]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
...