如何使用无点方法重新组织此数据

时间:2017-07-23 19:52:31

标签: javascript data-structures functional-programming ramda.js

我试图学习一些关于函数式编程和Ramda的知识,并且正在使用我最喜欢的数据结构操作来尝试一些免费的东西。很少或没有循环。我的输入数据:

const data = [{
    timeline_map: {
        "2017-05-06": 770,
        "2017-05-07": 760,
        "2017-05-08": 1250,
    }
}, {
    timeline_map: {
        "2017-05-06": 590,
        "2017-05-07": 210,
        "2017-05-08": 300,
    }
}, {
    timeline_map: {
        "2017-05-06": 890,
        "2017-05-07": 2200,
        "2017-05-08": 1032,
    }
}]

期望的输出:

[
  ["2017-05-06", 770, 590, 890, ...],
  ["2017-05-07", 760, 210, 2200, ...],
  ["2017-05-08", 1250, 300, 1032, ...]
]

这是我的代码:

const condense = x => x.timeline_map
var rest = []
const pluckDate = (obj, i) => {
  return [Object.keys(obj)[i]]
}
const getValues = value => {
  data.map(function () {
    return data.timeline_map
  })
}
const result = R.compose(
  R.values,
  R.mapObjIndexed(pluckDate),
  R.map(condense)
)
console.log(result(data))

到目前为止,我所得到的只是提取的日期:

[["2017-05-06"], ["2017-05-07"], ["2017-05-08"]]

这仍然远离解决方案我不知道我是否已经在"好"办法。什么是"功能" /" ramda" /"点免费"解决这个问题的方法?

JSBIN

4 个答案:

答案 0 :(得分:3)

这是一种方法。可能有一个更简单的一个:

const convert = pipe(
  pluck('timeline_map'), //=> [{"2017-05-06": 770, "2017-05-07": 760, ...}, ...]
  map(toPairs),          //=> [[["2017-05-06", 770], ["2017-05-07", 760], ...], ...]
  unnest,                //=> [["2017-05-06", 770], ["2017-05-07", 760], ...]
  groupBy(head),         //=> {"2017-05-06": [["2017-05-06", 770], ["2017-05-06", 590], ...], "2017-05-07": [...], ...}
  map(map(last)),        //=> {"2017-05-06": [770, 590, 890], "2017-05-07": [760, 210, 2200], ...]
  toPairs,               //=> [["2017-05-06", [770, 590, 890]], ["2017-05-07", [760, 210, 2200]], ...]
  map(apply(prepend))    //=> [["2017-05-06", 770, 590, 890], ["2017-05-07", 760, 210, 2200], ...]
)
convert(data)

这涉及许多不同的步骤,我已经展示了每个生成的数据类型。

您可以在 Ramda REPL 上看到这一点。

答案 1 :(得分:2)

只是为了好玩,我想尝试在一个循环中使用两个嵌套的reducer进行。有三个原因我认为我会分享我的想法:

  • 我认为最好的出发点始终是写出来而不用担心风格,直到你得到你想要的结果,然后再尝试去“免费点”:)
  • 很高兴看到转换基本上是两个reduce操作的组合:从{ timeline_map }[entries],从[entries][[key, ...values]] < / LI>
  • 它表明想要在一个循环中完成所有事情需要你做丑陋的事情:D

可能有趣的是看两个减少的,变异的Map方法是否也可以组成一个无点的Ramda函数......但是我不是那个想出来的人。

// from [[key, value]] to Map(key, [key, values])
const timeLineReducer = (map, [key, value]) => 
  map.set(
    key, 
    (map.get(key) || [key]).concat(value)
  );

// From [{ key: value }] to Map(key, [key, values])
const dataReducer = (map, { timeline_map }) =>
   Object
      .entries(timeline_map)
      .reduce(timeLineReducer, map);

const transformData = data => Array.from(
    data
      .reduce(dataReducer, new Map())
      .values()
  );
  
console.log(transformData(getData()))
  


// data
function getData() { return [{timeline_map:{"2017-05-06":770,"2017-05-07":760,"2017-05-08":1250}},{timeline_map:{"2017-05-06":590,"2017-05-07":210,"2017-05-08":300}},{timeline_map:{"2017-05-06":890,"2017-05-07":2200,"2017-05-08":1032}}]; }

编辑:出于好奇,我想去点免费编程。一个有趣的谜题,但我不能说我真的很感激结果。

之前没试过,所以我甚至不确定它 是否免费。但它有效!

// e.g.: [2] -> [1] -> [1, 2]
const fConcat = flip(concat);

// e.g. : [1, 3] -> [1, 2] -> [1, 2, 3]
const fConcatTail = useWith(fConcat, [tail, identity]);

// e.g.: secNil(1, null) -> true
const secNil = compose(isNil, nthArg(1));

// e.g.: fConcatTailIf([0, 1], null)   -> [0, 1]
// e.g.: fConcatTailIf([0, 2], [0, 1]) -> [0, 1, 2]
const fConcatTailIf = ifElse(
  secNil,
  clone,
  fConcatTail
);

// e.g.: ["a", 1] -> lensProp("a")
const kvpKeyLensProp = compose(lensProp, head);


// e.g.: ["a", 1] -> {}              -> { a: ["a", 1] }
// e.g.: ["a", 2] -> { a: ["a", 1] } -> { a: ["a", 1, 2]}
const handleKVP = compose(
  apply(over),                          // create over that waits for {}
  ap([kvpKeyLensProp, fConcatTailIf]),  // ap with arg.
  of                                    // wrap argument in array
);

// (kvp, map) => handleKVP(kvp)(map);
const mergeKVPWithMap = uncurryN(2, handleKVP);

// Flip because reduce is inverted ((map, kvp) => ...)
// e.g.: {} -> ["a", 1] -> { a: ["a", 1]}
const entryReducer = flip(mergeKVPWithMap);
 

// e.g.: {}              -> [ ["a", 1] ] -> { a: ["a", 1] }
// e.g.: { a: ["a", 1] } -> [ ["a", 2] ] -> { a: ["a", 1, 2] }
const timelineReducer = reduce(entryReducer);

// e.g. { timeline_map: { a: 1 } } -> [ [ "a", 1 ] ]
const entriesFromData = compose(toPairs, prop("timeline_map"));

// Decorate 2nd argument of timelineReducer with entriesFromData
const dataReducer = useWith(timelineReducer, [identity, entriesFromData]);

// Return only the values from our composed object
const transformData = compose(values, reduce(dataReducer, { }));
  
// Call our transformation with our data
transformData(getData())
  


// data
function getData() { return [{timeline_map:{"2017-05-06":770,"2017-05-07":760,"2017-05-08":1250}},{timeline_map:{"2017-05-06":590,"2017-05-07":210,"2017-05-08":300}},{timeline_map:{"2017-05-06":890,"2017-05-07":2200,"2017-05-08":1032}}]; }

Ramda REPL!

中试用

答案 2 :(得分:1)

为了演示目的,这是另一种解决方案。

 const concatAll = reduce(mergeWith(concat), {});
 const wrapInArray = value => [value];
 const zipKeysWithValues = (arr) => zip(keys(arr), values(arr));

 const res1 = pipe(
  pluck('timeline_map'),
  map(map(wrapInArray)),
  concatAll,
  zipKeysWithValues,
  map(flatten),
 )(data);

 const res2 = compose(
    map(flatten), 
    zipKeysWithValues, 
    concatAll, 
    map(map(wrapInArray)), 
    pluck('timeline_map'))(data);

您也可以同时使用pipecompose。对于记录,在任意两个步骤之间使用tap(console.log)

答案 3 :(得分:0)

使用纯js的最快方法

var data = [{
    timeline_map: {
        "2017-05-06": 770,
        "2017-05-07": 760,
        "2017-05-08": 1250,
    }
}, {
    timeline_map: {
        "2017-05-06": 590,
        "2017-05-07": 210,
        "2017-05-08": 300,
    }
}, {
    timeline_map: {
        "2017-05-06": 890,
        "2017-05-07": 2200,
        "2017-05-08": 1032,
    }
}];

var mapper = {};
var output = [];
data.forEach((_node) => {
    for (let key in _node.timeline_map) {
        if (!mapper[key]) {
            mapper[key] = [];
        }
        mapper[key].push(_node.timeline_map[key]);
    }
});
for (let key in mapper) {
    let row = [key];
    row = row.concat(mapper[key]);
    output.push(row);
}
console.log(mapper); //"{"2017-05-06":[770,590,890],"2017-05-07":[760,210,2200],"2017-05-08":[1250,300,1032]}"

console.log(output); //[["2017-05-06",770,590,890],["2017-05-07",760,210,2200],["2017-05-08",1250,300,1032]]