Условно разделенный массив на основе логики последовательности с использованием ramda

#javascript #ramda.js

#javascript #ramda.js

Вопрос:

У меня есть массив фотографий, каждая из которых имеет aspectRatio . Я хотел бы разделить массив на меньшие массивы разной длины, зависящие от aspectRatio .

 const photos = [
  { fluid: { aspectRatio: 1.5 } },
  { fluid: { aspectRatio: 1.5 } },
  { fluid: { aspectRatio: 0.67 } },
  { fluid: { aspectRatio: 0.67 } },
  { fluid: { aspectRatio: 0.67 } },
  { fluid: { aspectRatio: 0.67 } },
  { fluid: { aspectRatio: 1.5 } },
  { fluid: { aspectRatio: 1.5 } },
  { fluid: { aspectRatio: 0.67 } }
]
  

В этом случае я хотел бы разделить массив на каждые 2 элемента по умолчанию, а затем на 3 элемента, когда есть 3 элемента aspectRatio < 1 подряд.

То, с чем я хотел бы остаться, это:

 const result = [
  [
    { fluid: { aspectRatio: 1.5 } }, 
    { fluid: { aspectRatio: 1.5 } }
  ],
  [
    { fluid: { aspectRatio: 0.67 } },
    { fluid: { aspectRatio: 0.67 } },
    { fluid: { aspectRatio: 0.67 } },
  ],
  [
    { fluid: { aspectRatio: 0.67 } }, 
    { fluid: { aspectRatio: 1.5 } }
  ],
  [
    { fluid: { aspectRatio: 1.5 } }, 
    { fluid: { aspectRatio: 0.67 } }
  ],
]
  

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

Ответ №1:

Вы можете использовать Array.reduce() для итерации массива и добавления нового подмассива в накопитель в соответствии с представленными вами правилами. Всегда помещайте текущий элемент в последний подмассив:

 const photos = [{"fluid":{"aspectRatio":1.5}},{"fluid":{"aspectRatio":1.5}}, {"fluid":{"aspectRatio":1.5}},{"fluid":{"aspectRatio":0.67}},{"fluid":{"aspectRatio":0.67}},{"fluid":{"aspectRatio":0.67}},{"fluid":{"aspectRatio":0.67}},{"fluid":{"aspectRatio":1.5}},{"fluid":{"aspectRatio":1.5}},{"fluid":{"aspectRatio":0.67}}]

const allAspectsUnder1 = arr => arr.every(o => o.fluid.aspectRatio < 1)

const result = photos.reduce((r, o) => {
  const last = r[r.length - 1]

  if (!last ||
    last.length === 3 ||
    (last.length === 2 amp;amp; !allAspectsUnder1(last))
  ) r.push([])

  r[r.length - 1].push(o)

  return r
}, [])

console.log(result)  

И это эквивалентная идея в Ramda:

 const { pipe, length, equals, allPass, any, pathSatisfies, lt, last, anyPass,  isNil, reduce, ifElse, init } = R

const lengthEquals = len => pipe(length, equals(len))

const length2AndLargeAspects = allPass([
  lengthEquals(2),
  any(pathSatisfies(lt(1), ['fluid', 'aspectRatio'])),
])

const shouldAddSub = pipe(
  last,
  anyPass([
    isNil,
    length2AndLargeAspects,
    lengthEquals(3),
  ])
)

const fn = reduce(ifElse(
  shouldAddSub, 
  (a, o) => [...a, [o]],
  (a, o) => [...init(a), [...last(a), o]],
), [])

const photos = [{"fluid":{"aspectRatio":1.5}},{"fluid":{"aspectRatio":1.5}}, {"fluid":{"aspectRatio":1.5}},{"fluid":{"aspectRatio":0.67}},{"fluid":{"aspectRatio":0.67}},{"fluid":{"aspectRatio":0.67}},{"fluid":{"aspectRatio":0.67}},{"fluid":{"aspectRatio":1.5}},{"fluid":{"aspectRatio":1.5}},{"fluid":{"aspectRatio":0.67}}]

const result = fn(photos)

console.log(result)  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>  

Ответ №2:

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

 const group = (ps, rs = []) => ps.length == 0
  ? rs
  : ps.length < 3
    ? rs.concat([ps])
    : ps.slice(0, 3).every(p => p.fluid.aspectRatio < 1)
      ? group(ps.slice(3), rs.concat([ps.slice(0, 3)]))
      : group(ps.slice(2), rs.concat([ps.slice(0, 2)]))

const photos = [{fluid: {aspectRatio: 1.5}}, {fluid: {aspectRatio: 1.5}}, {fluid: {aspectRatio: 0.67}}, {fluid: {aspectRatio: 0.67}}, {fluid: {aspectRatio: 0.67}}, {fluid: {aspectRatio: 0.67}}, {fluid: {aspectRatio: 1.5}}, {fluid: {aspectRatio: 1.5}}, {fluid: {aspectRatio: 0.67}}]

console.log(group(photos))  

Ответ №3:

Используя простой do/while цикл. Разделите подмассивы на 3 и проверьте, не больше ли единицы, и вернитесь к 2, если есть

 let res=[], sIdx = 0;

do {

 const arr = photos.slice(sIdx, sIdx   3); 
 if(arr.length > 2 amp;amp; arr.some(({ fluid:{aspectRatio}}) => aspectRatio >= 1 ) ){
    arr.pop();
 }
 res.push(arr);
 sIdx  = arr.length;
 
} while ( sIdx < photos.length)

console.log(res)  
 .as-console-wrapper {	max-height: 100%!important;}  
 <script>

const photos = [
  { fluid: { aspectRatio: 1.5 } },
  { fluid: { aspectRatio: 1.5 } },
  { fluid: { aspectRatio: 0.67 } },
  { fluid: { aspectRatio: 0.67 } },
  { fluid: { aspectRatio: 0.67 } },
  { fluid: { aspectRatio: 0.67 } },
  { fluid: { aspectRatio: 1.5 } },
  { fluid: { aspectRatio: 1.5 } },
  { fluid: { aspectRatio: 0.67 } }
]

</script>