需求
想要統(tǒng)計環(huán)比指標((本期數(shù)量-上期數(shù)量)/上期數(shù)量*100%) 假設(shè)下面是統(tǒng)計9月份的數(shù)據(jù) 如下所示
| 品牌 | 數(shù)量 | 環(huán)比 |
|---|---|---|
| Bosh | 1561 | 311.87% |
| Siemens | 2278 | -75.24% |
問題
查詢的時候 需要同時查詢8月的數(shù)據(jù) 統(tǒng)計出8月的數(shù)量 然后才能進行環(huán)比指標的計算
{ "count" : 379.0, "brand" : "Bosch", "month" : "2017-08" }
{ "count" : 1561.0, "brand" : "Bosch", "month" : "2017-09" }
{ "count" : 9202.0, "brand" : "Siemens", "month" : "2017-08" }
{ "count" : 2278.0, "brand" : "Siemens", "month" : "2017-09" }
怎么轉(zhuǎn)換得到上圖的結(jié)果呢? 即
{ "count" : 379.0, "brand" : "Bosch", "month" : "2017-08" }
{ "count" : 1561.0, "brand" : "Bosch", "month" : "2017-09" }
{ "count" : 9202.0, "brand" : "Siemens", "month" : "2017-08" }
{ "count" : 2278.0, "brand" : "Siemens", "month" : "2017-09" }
==>
{ "count" : 1561.0, "brand" : "Bosch", "month" : "2017-09","huanbi": 311.87 }
{ "count" : 2278.0, "brand" : "Siemens", "month" : "2017-09","huanbi":-75.24 }
我以為挺好實現(xiàn)的 沒想到還挺折騰的 代碼如下
Map<String,Object> record1 = new HashMap(ImmutableMap.of("count", 379, "brand", "Bosch", "month", "2017-08"));
Map<String,Object> record2 = new HashMap(ImmutableMap.of("count", 1561, "brand", "Bosch", "month", "2017-09"));
Map<String,Object> record3 = new HashMap(ImmutableMap.of("count", 9202, "brand", "Siemens", "month", "2017-08"));
Map<String,Object> record4 = new HashMap(ImmutableMap.of("count", 2278, "brand", "Siemens", "month", "2017-09"));
Map<String,Object> record5 = new HashMap(ImmutableMap.of("count", 2278, "brand", "foo", "month", "2017-09"));
List<Map<String, Object>> queryResult = Lists.newArrayList(record1, record4, record3, record2, record5);
// 先按品牌和日期排序
queryResult.sort((o1,o2)->{
int result = 0;
String[] keys = {"brand", "month"};
for (String key : keys) {
String val1 = o1.get(key).toString();
String val2 = o2.get(key).toString();
result = val1.compareTo(val2);
if(result != 0){
return result;
}
}
return result;
});
// 再按品牌分組
Map<String, List<Map<String, Object>>> brand2ListMap = queryResult.stream().collect(groupingBy(m -> m.get("brand").toString(), toList()));
/**
* 每組中第一條肯定是上一月的 找到上月的數(shù)目
* 第二條記錄是本月的 找到本月的數(shù)據(jù)
* 計算環(huán)比 本期記錄中添加環(huán)比
* 同時刪除上一條記錄
*/
for (String key : brand2ListMap.keySet()) {
List<Map<String, Object>> recordList = brand2ListMap.get(key);
if (recordList.size() > 1) {
Map<String, Object> prevRecord = recordList.get(0);
Map<String, Object> currentRecord = recordList.get(1);
Integer prevCount = (Integer) prevRecord.get("count");
Integer currentCount = (Integer) currentRecord.get("count");
BigDecimal huanbi = BigDecimal.valueOf((currentCount - prevCount) * 100).divide(BigDecimal.valueOf(prevCount), 2, ROUND_HALF_DOWN);
currentRecord.put("huanbi", huanbi);
recordList.remove(0);
}else{
// 不存在上期記錄 環(huán)比默認為0
recordList.get(0).put("huanbi", 0);
}
}
// 生成一個新的List 只包含本期記錄
List<Map<String, Object>> processedResult = new ArrayList(brand2ListMap.values().stream().flatMap(list->list.stream()).collect(toList()));
// 按照品牌排序
processedResult.sort(Comparator.comparing(o -> o.get("brand").toString()));
processedResult.forEach(System.out::println);
輸出結(jié)果如下
{count=1561, month=2017-09, brand=Bosch, huanbi=311.87}
{count=2278, month=2017-09, brand=Siemens, huanbi=-75.24}
{count=2278, month=2017-09, brand=foo, huanbi=0}
應(yīng)該不是我想的復雜了吧?應(yīng)該沒有更簡單的方案了吧?
剛剛看到你的私信。如果用MongoDB解決的話方法如下:
// 測試數(shù)據(jù)
db.test.insert([
{ "count" : 379.0, "brand" : "Bosch", "month" : "2017-08" },
{ "count" : 1561.0, "brand" : "Bosch", "month" : "2017-09" },
{ "count" : 9202.0, "brand" : "Siemens", "month" : "2017-08" },
{ "count" : 2278.0, "brand" : "Siemens", "month" : "2017-09" }
]);
// 運算方法
db.test.aggregate([
{$match: {month: {$in: ["2017-08", "2017-09"]}}},
{$sort: {month: 1}},
{$group: {_id: "$brand", lastMonth: {$first: "$count"}, thisMonth: {$last: "$count"}, month: {$last: "$month"}}},
{$project: {brand: 1, ratio: {$divide: [{$subtract: ["$thisMonth", "$lastMonth"]}, "$lastMonth"]}}}
])
// 結(jié)果
{ "_id" : "Siemens", "ratio" : -0.7524451206259509 }
{ "_id" : "Bosch", "ratio" : 3.1187335092348283 }
為了最好的效果,需要添加一些索引以優(yōu)化查詢:
db.test.createIndex({month: 1});
另外建議你日期都用Date,不要用字符串,這是個良好的習慣。就算現(xiàn)在用起來沒什么區(qū)別,早晚也是會需要它是個日期的。
@Test
public void test02() {
List<DemoEntity> list = Arrays.asList(
new DemoEntity(379, "Bosch", "2017-08"),
new DemoEntity(1561, "Bosch", "2017-09"),
new DemoEntity(9202, "Siemens", "2017-08"),
new DemoEntity(2278, "Siemens", "2017-09")
);
//按brand和month分組 key自己定義
Map<String, Integer> map = list.stream()
.collect(Collectors.toMap(o -> o.getBrand() + "||" + o.getMonth(), DemoEntity::getCount));
list.forEach(entity -> {
//獲取上月份的數(shù)量
Integer count = map.get(entity.getBrand() + "||" + getPreMonth(entity.getMonth()));
Optional.ofNullable(count)
.map(o -> BigDecimal.valueOf(entity.getCount() - count)
.multiply(BigDecimal.valueOf(100))
.divide(BigDecimal.valueOf(count), 2, BigDecimal.ROUND_HALF_UP)
.doubleValue())
.ifPresent(entity::setPercent);
});
//篩選打印某一月份
list.stream()
.filter(entity -> Objects.equals(entity.getMonth(), "2017-09"))
.map(JSON::toJSONString)
.forEach(System.out::println);
}
/**
* 獲取指定月份的上一月日期
*/
private String getPreMonth(String month) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
return sdf.format(DateUtils.addMonths(sdf.parse(month), -1));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
控制臺輸出:
{"brand":"Bosch","count":1561,"month":"2017-09","percent":311.87}
{"brand":"Siemens","count":2278,"month":"2017-09","percent":-75.24}
如果需要排序,可以在最后的結(jié)果做一層排序。
北大青鳥APTECH成立于1999年。依托北京大學優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國家
達內(nèi)教育集團成立于2002年,是一家由留學海歸創(chuàng)辦的高端職業(yè)教育培訓機構(gòu),是中國一站式人才培養(yǎng)平臺、一站式人才輸送平臺。2014年4月3日在美國成功上市,融資1
北大課工場是北京大學校辦產(chǎn)業(yè)為響應(yīng)國家深化產(chǎn)教融合/校企合作的政策,積極推進“中國制造2025”,實現(xiàn)中華民族偉大復興的升級產(chǎn)業(yè)鏈。利用北京大學優(yōu)質(zhì)教育資源及背
博為峰,中國職業(yè)人才培訓領(lǐng)域的先行者
曾工作于聯(lián)想擔任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔任項目經(jīng)理從事移動互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍懿科技有限責任公司從事總經(jīng)理職務(wù)負責iOS教學及管理工作。
浪潮集團項目經(jīng)理。精通Java與.NET 技術(shù), 熟練的跨平臺面向?qū)ο箝_發(fā)經(jīng)驗,技術(shù)功底深厚。 授課風格 授課風格清新自然、條理清晰、主次分明、重點難點突出、引人入勝。
精通HTML5和CSS3;Javascript及主流js庫,具有快速界面開發(fā)的能力,對瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁制作和網(wǎng)頁游戲開發(fā)。
具有10 年的Java 企業(yè)應(yīng)用開發(fā)經(jīng)驗。曾經(jīng)歷任德國Software AG 技術(shù)顧問,美國Dachieve 系統(tǒng)架構(gòu)師,美國AngelEngineers Inc. 系統(tǒng)架構(gòu)師。