Ramda:将列表拆分为段

Ramda: Split a list to segments

如果我们有一个列表,例如:

[
  {
    type: 'a',
  },
  {
    type: 'a',
  },
  {
    type: 'b',
  },
  {
    type: 'a',
  }
]

... 我们想对其进行分段以创建一个列表,这样新列表由初始列表的每个部分组成,这里按类型拆分,如下所示:

[
  [
    {
      type: 'a',
    },
    {
      type: 'a',
     },
  ],
  [
    {
      type: 'b',
    },
  ],
  [
    {
      type: 'a',
    }
  ]
]

我想创建一个通用的 'segmenting' 函数,它使用一个函数来比较两个项目,并确定是否需要一个新的细分。在这里,该函数的 'segmenter' 只是比较类型。

我可以用 vanilla javascript、但是有没有用 Ramda 做到这一点的好方法

const data = [
  {
    type: 'a',
  },
  {
    type: 'a',
  },
  {
    type: 'b',
  },
  {
    type: 'a',
  }
];

const segmentBy = segmenter => items => {
  const segmentReducer = (prev = [], curr) => {
    let lastSegment = [];
    let lastItem = null;
    
    try {
      lastSegment = prev[prev.length - 1];
      lastItem = lastSegment[lastSegment.length - 1];
    } catch (e) {
      return [...prev, [curr]];
    }
    const requiresNewSegment = segmenter(lastItem, curr);
    
    if (requiresNewSegment) {
      return [...prev, [curr]];
    }
    
    return [...prev.slice(0, prev.length - 1), [...lastSegment, curr]];
  };
  
  return items.reduce(segmentReducer, []);
};

const segmentByType = segmentBy((a, b) => a.type !== b.type);
const segments = segmentByType(data);
  
console.dir(segments);

使用 Ramda,您可以使用 R.groupWith:

Takes a list and returns a list of lists where each sublist's elements are all satisfied pairwise comparison according to the provided function. Only adjacent elements are passed to the comparison function.

const data = [{"type":"a"},{"type":"a"},{"type":"b"},{"type":"a"}];

const segmentByType = R.groupWith(R.eqBy(R.prop('type')));
const segments = segmentByType(data);
  
console.dir(segments);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

在原版中,主要问题是何时向累加器添加新的子数组。当它是第一项时,您需要添加另一个子数组,或者如果 segmenter returns true.

const data = [{"type":"a"},{"type":"a"},{"type":"b"},{"type":"a"}];

const segmentBy = segmenter => items =>
  items.reduce((r, item, i, arr) => {
    if(i === 0 || segmenter(item, arr[i - 1])) r.push([]);
    
    r[r.length - 1].push(item);
    
    return r;
  }, []);

const segmentByType = segmentBy((a, b) => a.type !== b.type);
const segments = segmentByType(data);
  
console.dir(segments);