【问题标题】:Iterating vertical values in a collection to horizontal values in another collection将集合中的垂直值迭代到另一个集合中的水平值
【发布时间】:2013-12-20 09:53:07
【问题描述】:

我的 java 程序中有一些字符串的字符串列表,如下所示:

List<String> input = new ArrayList<String>();

    input.add("2013-07-31,A,47");
    input.add("2013-06-28,A,52");
    input.add("2013-05-31,A,63");

    input.add("2013-07-31,B,47");
    input.add("2013-06-28,B,54");
    input.add("2013-05-31,B,91");

    input.add("2013-07-31,C,78");
    input.add("2013-06-28,C,24");

    input.add("2013-07-31,D,99");
    input.add("2013-05-31,D,82");

这里在每个字符串中,逗号分隔的第一个标记代表日期,第二个标记是产品名称,第三个标记是价格。

我想使用上面的列表生成下表格式的 excel 报告:

Prod   |           Price
---------------------------------------------
  XXX    |   M1        M2        M3
---------------------------------------------
  A      |   47        52        63
  B      |   47        54        91
  C      |   78        24        00
  D      |   99        00        82
---------------------------------------------

其中 M1 是 2013 年 7 月 31 日,M2 是 2013 年 6 月 28 日,而 M3 是 2013 年 5 月 31 日。

为了实现这一点,我的输出集合应该是一个包含成员字符串的字符串列表,如下所示:

  A, 47, 52, 63
  B, 47, 54, 91
  C, 78, 24, 00
  D, 99, 00, 82

请注意,在输入字符串中,日期不存在值,例如产品 C 为 2013-05-31,产品 D 为 2013-06-28,输出字符串中的值应默认为 00。

因此,在输出字符串中,这样的缺失值被视为 00。

如何迭代我的输入集合,该集合具有产品的按日期垂直值,以获得输出集合,它是 3 个日期的产品水平值?

感谢阅读!

【问题讨论】:

  • 拆分字符串并创建产品对象,这样会容易得多。
  • @peeskillet:为第一个字符串创建迭代器.. :) 我应该粘贴它吗?这不是作业问题...
  • 您想从新列表中完全省略日期吗?
  • @peeskillet:是的。输入列表首先维护具有最高日期的顺序。我已经能够以这种方式获得输入。所以我们可以轻松地假设在输出字符串中,第一个值是第一个日期,依此类推。但是在创建时,我们必须看到如果没有日期,则该值应取为 00。

标签: java string list


【解决方案1】:

试试这个代码。您可以轻松地对其进行进一步调整以满足您的确切需求。

    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;


    public class Test002 {

        public static void main(String[] args) throws Exception {
            List<String> input = new ArrayList<String>();

            input.add("2013-07-31,A,47");
            input.add("2013-06-28,A,52");
            input.add("2013-05-31,A,63");

            input.add("2013-07-31,B,47");
            input.add("2013-06-28,B,54");
            input.add("2013-05-31,B,91");

            input.add("2013-07-31,C,78");
            input.add("2013-06-28,C,24");

            input.add("2013-07-31,D,99");
            input.add("2013-05-31,D,82");

            HashSet<Date> dates = new HashSet<Date>();
            HashSet<String> products = new HashSet<String>();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            HashMap<String, Integer> counts = new HashMap<String, Integer>();

            for (String s : input){
                String[] str = s.split(",");
                dates.add(sdf.parse(str[0]));
                products.add(str[1]);
                counts.put(str[0] + "#" + str[1], Integer.parseInt(str[2]));
            }

            for (String p : products){
                for (Date dt : dates){
                    Integer cnt = counts.get(sdf.format(dt) + "#" + p);
                    if (cnt == null){
                        System.out.print("00 ");
                    }else{
                        System.out.printf("%2d ", cnt);
                    }
                }
                System.out.println();
            }
        }

    }

【讨论】:

  • +1 for counts.put(str[0] + "#" + str[1], Integer.parseInt(str[2]));这条线......拯救了一天! :)
  • @Vicky 只是一个简单的分隔符技巧,因为您不会在程序计算中的其他任何地方使用 # 。所以我把它放在那里只是为了避免任何麻烦。
【解决方案2】:

我认为列表是存储值的正确数据结构,因为它是扁平的。我们对您的域知之甚少,但如果允许的日期超过三个,您最终可能会得到一张包含无数列的表格。

事实上,如果这是一个时间序列,则一系列工作表是最好的表示形式,并且可能是具有聚合周期(季度、周、月)和平均值的工作表。所以我会为你的域建模如下:

class TimeSeries {
  String itemDescription;
  Collection<Sample> samples;
}

class Sample {
  Date date;
  BigInteger price;
}

然后,我会将您输入的字符串列表转换为 TimeSeries 对象列表,随后您将按日期聚合,可能会在同一时期有多个样本可用时进行平均,并跳过空样本。最后将消化后的结果绘制到 Excel 表格上。

【讨论】:

  • 不。日期不超过 3 个。
【解决方案3】:

试试这个。它首先使用地图为您提供所需的输出,然后再转换回列表

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class test1 {

    public static void main(String[] args) {
        List<String> input = new ArrayList<String>();

        input.add("2013-07-31,A,47");
        input.add("2013-06-28,A,52");
        input.add("2013-05-31,A,63");

        input.add("2013-07-31,B,47");
        input.add("2013-06-28,B,54");
        input.add("2013-05-31,B,91");

        input.add("2013-07-31,C,78");
        input.add("2013-06-28,C,24");

        input.add("2013-07-31,D,99");
        input.add("2013-05-31,D,82");

        Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();

        for (String s : input) {
            String[] tokens = s.split(",");
            if (!map.containsKey(tokens[1])) {
                ArrayList<String> list = new ArrayList<String>();
                list.add(tokens[2]);
                map.put(tokens[1], list);
            } else {
                ArrayList<String> list = map.get(tokens[1]);
                list.add(tokens[2]);
                map.put(tokens[1], list);
            }
        }

        ArrayList<String> newList = new ArrayList<String>();
        for (Map.Entry<String, ArrayList<String>> entry : map.entrySet()) {
            StringBuilder sb = new StringBuilder();
            sb.append(entry.getKey() + ",");
            for (String s : entry.getValue()) {
                sb.append(s + ",");
            }
            newList.add(sb.toString());
        }

        Collections.sort(newList);

        for (String s : newList) {
            System.out.println(s);
        }
    }
}

输出

A,47,52,63,
B,47,54,91,
C,78,24,
D,99,82,

抱歉,我只是注意到您的注释,但我并不能真正理解您的意思,所以我不确定如何生成零

【讨论】:

  • 在 else 部分 map.put(tokens[1], list); 不是必需的。
【解决方案4】:

这就是我在假设下的做法

  • 每个日期最多允许一个样本
  • 您的渲染代码决定选择哪些日期

这种方法不会在渲染前构建中间数据结构,而是在从输入列表中读取单元格时立即绘制:

// Maps items IDs to the row index in the excel sheet
Map<String, Integer> items = new HashMap<>();
// Maps the string representation of the date to the column index in the sheet
Map<String, Integer> columns = new HashMap<>();

public void process(List<String> input) {
  for (String line: input) {
    String[] desc = input.split(",");
    Integer rowIdx = items.get(desc[1]);
    if (rowIdx == null) {
      rowIdx = drawNewItemAndGetRowIndex(desc[1]);
    }
    drawPrice(rowIdx, columns.get(desc[0]), desc[2]);
  }
}

在运行此代码之前,您需要通过放置您感兴趣的日期来初始化 Excel 工作表和 columns 映射。注意我使用日期的字符串表示而不是实际的 Date 对象,因为解析不是为您的目的而需要。

我将drawNewItemAndGetRowIndex 的实现留给你,但它基本上是在Excel 工作表中创建一个新行,在items 映射中注册索引,绘制项目标题并返回行索引。

【讨论】:

    猜你喜欢
    • 2012-12-28
    • 2013-07-31
    • 1970-01-01
    • 1970-01-01
    • 2018-12-11
    • 2012-08-06
    • 2018-02-09
    • 1970-01-01
    • 2016-01-25
    相关资源
    最近更新 更多