字符串和数字的范围分组列表

时间:2019-08-13 13:54:22

标签: java arrays string sorting grouping

我有一个看起来像这样的列表

String[] lst = {BB,2,1,3,AA,DD,A3,A1,EE,A2,4);

我需要对该列表进行分组和排列,但是遇到了麻烦, 结果需要找到这样的东西

  

结果:(1-4),(A1-A3),(AA-BB),(DD-EE)

我想出的代码是这个

Map<Character, List<String>> collect;

collect = Arrays.stream(str).
flatMap(s -> Stream.of(s.split("[^a-zA-Z0-9]"))).
filter(s -> !s.trim().isEmpty()).
sorted().
collect(Collectors.groupingBy(s -> s.charAt(0)));

,但按首字母分组,这意味着AA与A1-A3分组,依此类推。 它不是那么琐碎的分组,我将不胜感激。

3 个答案:

答案 0 :(得分:0)

正如其他用户在评论中提到的那样,滚动自己的解决方案并不容易。要解决此特定问题,您可以执行类似的操作

    String[] str = {"BB","2","1","3","AA","DD","A3","A1","EE","A2","4"};
    Map<String, List<String>> collect;

    collect = Arrays.stream(str)
            .flatMap(s -> Stream.of(s.split("[^a-zA-Z0-9]")))
            .filter(s -> !s.trim().isEmpty())
            .sorted()
            .collect(Collectors.groupingBy(s -> {
                final StringBuilder groupKey = new StringBuilder();
                char first = s.charAt(0);
                if (Character.isAlphabetic(first)) {
                    if (first >= 'D') {
                        groupKey.append("ALPHA-HIGH");
                    } else {
                        groupKey.append("ALPHA-LOW");
                    }
                } else {
                    groupKey.append("NON-ALPHA");
                }
                if (s.length() == 2) {
                    char second = s.charAt(1);
                    if (Character.isAlphabetic(second)) {
                        if (first >= 'D') {
                            groupKey.append("_ALPHA-HIGH");
                        } else {
                            groupKey.append("_ALPHA-LOW");
                        }
                    } else {
                        groupKey.append("_NON-ALPHA");
                    }
                }
                return groupKey.toString();
            }));

这将为您提供所需的输出。请注意,使用键(字符串)代替单个字符。

这是怎么回事?您有许多不同的可能组,我将它们视为两个宏组:字母和非宏。在您的情况下,非字母内容 是数字。长度为2的字符串可以将第二个字符作为字母或数字。如果字母字符为D或更高,则被认为是“高”。

输出

四个小组:

NON-ALPHA: {1, 2, 3, 4}
ALPHA-LOW_NON-ALPHA: {A1, A2, A3}
ALPHA-HIGH_ALPHA-HIGH: {DD, EE}
ALPHA-LOW_ALPHA-LOW: {AA, BB}

答案 1 :(得分:0)

要建立这样的连续组,您首先需要定义一个函数,该函数将两个项标识为它们是否彼此跟随,即以连续顺序站立。例如。 “ 1”后跟“ 2”,但不跟“ 3”或“ A”;在您的示例中,“ AA”后跟“ BB”。有了这样的功能,您可以遍历已排序的列表,并比较相邻项之间的差异来决定是打开组,关闭组还是独立打印项目。

我将调用此类功能follows(String a, String b)。然后,建立组的算法很简单:

static String printGroups(String[] items) {
  Arrays.sort(items);      // strictly saying, sorting order must be consistent with `follows`
  boolean open = false;    // a group is open currently
  StringBuilder result = new StringBuilder();
  for (int i = 0; i < items.length; ++i) {
    if (!open && i > 0) {
      result.append(',');
    }
    if (i < items.length - 1 && follows(items[i], items[i + 1])) {
      if (!open) {
        // open a group
        result.append('(').append(items[i]).append('-');
        open = true;
      }
    } else if (open) {
      // close the group
      result.append(items[i]).append(')');
      open = false;
    } else {
      // print a standalone item
      result.append(items[i]);
    }
  }
  return result.toString();
}

follows函数已根据您的示例进行了调整(看起来很糟糕,您可以使用Java流或StringUtils或其他方法使其更加清晰/可读)-

static boolean follows(String a, String b) {
    if (a.length() != b.length() && a.length() == 0) {
        return false;
    }
    // AAA -> BBB
    if (allSame(a) && allSame(b) && (b.charAt(0) - a.charAt(0) == 1)) {
        return true;
    }
    // ABC1 -> ABC2
    // finding common prefix
    int p = 0;
    while (p < a.length() && a.charAt(p) == b.charAt(p)) {
        ++p;
    }
    return (p == a.length() - 1) && (b.charAt(p) - a.charAt(p) == 1);
}

static boolean allSame(String chars) {
    char s = chars.charAt(0);
    return chars.chars().allMatch(c -> s == c);
}

在那之后,您只需将文本拆分为项目和供稿即可:

printGroups("BB,2,1,3,AA,DD,A3,A1,EE,A2,4".split(","));  // (1-4),(A1-A3),(AA-BB),(DD-EE)

答案 2 :(得分:0)

这是我的解决方法。

这将适用于任何给定的AdjacentAwareComparator,只要它针对其值空间正确实现即可。以下comparator用于您定义的值空间。

您可以轻松地使getRanges接受List而不是数组,或者如果不想使用所有元素,则仅存储范围的第一个和最后一个:

import static java.lang.Character.isDigit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class Main {

    /**
     * Marker interface.
     * 
     * Implementors MUST adhere to all contracts of Comparator, and MUST return -1 or 1 if and only if
     * the compared values are adjacent to one another within the set of all possible values.
     */
    @FunctionalInterface public interface AdjacentAwareComparator<T> extends Comparator<T> {};

    /**
     * Assumes the input is valid in the defined value space.
     * 
     * Sort order: Digit (natural), Alpha+Digit (by alpha, then by digit), Alpha+Alpha (natural)
     */
    private static AdjacentAwareComparator<String> comparator = (x, y) -> {
        // uses 2 and -2 to compare values as non-adjacent
        if (x == null) return (y == null) ? 0 : -2;
        if (y == null) return 2;
        // both are not null...

        if (x.isEmpty()) return y.isEmpty() ? 0 : -2;
        if (y.isEmpty()) return 2;
        // both are at least length 1...

        char x1 = x.charAt(0), y1 = y.charAt(0);
        if (isDigit(x1)) return isDigit(y1) ? (x1 - y1) : -2;
        if (isDigit(y1)) return 2;
        // both start with letters...

        int d1 = x1 - y1; // delta between first chars
        char x2 = x.charAt(1), y2 = y.charAt(1);
        if (isDigit(x2)) return isDigit(y2) ? ((d1 == 0) ? (x2 - y2) : (d1 * 2)) : -2;
        if (isDigit(y2)) return 2;

        // the strings are double letters (eg. 'AA' and 'BB')
        return d1;
    };

    public static <T> List<List<T>> getRanges(T[] arr, AdjacentAwareComparator<T> comp) {
        if (arr.length == 0) {
            return new ArrayList<>();
        }
        List<List<T>> ranges = new ArrayList<>();
        List<T> range = new ArrayList<>();

        // sort using the custom Comparator
        Arrays.sort(arr, comp);
        T prev = arr[0];
        range.add(prev);

        // iterate through the sorted array
        for (int i = 1; i < arr.length; i++) {
            T curr = arr[i];
            int d = comp.compare(prev, curr);
            if (d < -1 || 1 < d) {
                // prev and curr are not adjacent nor equal, so start a new range
                ranges.add(range);
                range = new ArrayList<>();
            }
            range.add(curr);
            prev = curr;
        }
        ranges.add(range);
        return ranges;
    }

    public static void main(String[] args) {
        String[] arr = {"4","1","BB","ZZ","A1","5","A5","FF","3","B2","A2","B1","AA"};
        for (List<String> range : getRanges(arr, comparator)) {
            System.out.println("{" + String.join(", ", range) + "}");
        }
        // prints:
        //   {1}
        //   {3, 4, 5}
        //   {A1, A2}
        //   {A5}
        //   {B1, B2}
        //   {AA, BB}
        //   {FF}
        //   {ZZ}
    }
}