This seems like it should be pretty simple but my searching hasn't turned anything up.
I have an array of objects that looks like this:
[{"A":4,"B":2},{"A":2,"B":1},{"A":3,"B":1},{"A":2,"B":1,"C":1}]
I want to flatten it down to something that looks like this (what I would think of as a reduce function):
{"A": 11, "B": 5, "C": 1}
The starting array of objects is the product of the transformation of a much larger and more complex starting object. The important detail is that the keys themselves are of arbitrary values (i.e. you might see 'D's and 'E's but the code itself needs to be blind to this). I've gotten to this point using Underscore.js and I'm hoping to find similarly clean, functional-style solution.
Get the key/value pairs and update the sum.
var data = [{ A: 4, B: 2 }, { A: 2, B: 1 }, { A: 3, B: 1 }, { A: 2, B: 1, C: 1 }],
result = data.reduce((r, o) => (Object.entries(o).forEach(([k, v]) => r[k] = (r[k] || 0) + v), r), {});
console.log(result);
Related
I have the following object
const d = {
a: 1,
b: 2,
c: 3,
}
What a I want to do is something like this:
const type = R.ifElse(R.isEmpty(R.prop('a')), "company", "personal")(d)
The equivalent in pure javascript would be
const type = d.a ? "personal" : "company"
How can I do this with ramda?
(Disclaimer: I have literally no experience with Ramda.js, I just looked it up and tried out a little bit. There may be a better solution)
You can use R.always instead of the strings, as it expects functions there. For checking if the object has a property you can use R.has
const d = {
a: 1,
b: 2,
c: 3,
}
const x = R.ifElse(R.has('a'), R.always("company"), R.always("personal"))(d)
I have two immutable map:
const first_map = Map({a: 1, b: 2)}
const second_map = Map({a: 1, b: 3)}
How to get the difference?
I should get:
Map({b: 3})
One possible approach is using Map.filter() - which is actually Collection.Keyed.filter() - on the second map with one condition: for a given element of this Map, there should be no element in another Map with the same key. For example:
const Map = Immutable.Map;
const first_map = Map({a: 1, b: 2, c: 4});
const second_map = Map({a: 1, b: 3, d: 5});
const diff = second_map.filter((v, k) => first_map.get(k) !== v);
console.log(diff.toString()); // Map { "b": 3, "d": 5 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.min.js"></script>
Strictly speaking, one should check for has first - as Map.get(key) returns undefined if there's no such key, but there might be valid cases for Map containing undefined. Still, I'd encourage using null for such values.
This question already has answers here:
JavaScript get elements from an object array that are not in another
(5 answers)
Closed 3 years ago.
I want an array that contains objects from the scrape object that are not present in the old object. The arrays I'm actually working with contains nearly 100 objects.
The code below works, but I wonder if there's a more efficient way of getting the same result?
var old = [
{a: 6, b: 3},
{a: 1, b: 1},
{a: 3, b: 3}
]
var scrape = [
{a: 1, b: 1},
{a: 5, b:5}
]
var nogood = []
var good =[]
scrape.forEach(es => {
old.forEach(e => {
if(e.a == es.a) {
nogood.push(es)
}
})
})
console.log(nogood)
nogood.forEach(main =>
good = scrape.filter(e=> e.a!=main.a)
)
console.log(good)
This is what I expect and what I'm getting:
good = {a:5, b:5}
Personally I would approach this with:
const old = [
{a: 6, b: 3},
{a: 1, b: 1},
{a: 3, b: 3}
];
const scrape = [{a: 1, b: 1}, {a: 5, b:5}];
for (const item of old) {
for (const i in scrape) {
if (JSON.stringify(item) === JSON.stringify(scrape[i])) {
scrape.splice(i, 1); //delete the previously scraped item
}
}
}
console.log(scrape); //{a: 5, b:5}
The benefits to this approach are:
You don't care what properties the objects you're comparing have,
you just care about whether they're identical.
It's fast
(comparing JSON is generally faster than traversing the objects to
compare each property).
It's more succinct to splice the scrape
array rather than adding the 'good' and 'nogood' arrays to arrive at
a filtered scrape array.
Possible deal breaker is if the objects you're comparing contain methods, in which case comparing them via JSON is not the correct approach.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
If we have arrays old and scrape to be of size M and N, respectively, all traditional approaches has the complexity of O(M * N) because you need to compare each entry within array scrape with the ones exists in array old to find out whether matches or not.
The second and more efficient approach is to create a hash table on first array, typically on bigger one (old here), and iterate over the second one (scrape here) which has the complexity of O(M + N).
If the size of M and N be as big as enough, the differences show themselves. As an example if M=100 and N=200, the former one needs to compare 20000 objects but the later one needs just 300 comparisons.
please take a look at this code:
const old = [
{a: 6, b: 3},
{a: 1, b: 1},
{a: 3, b: 3}
]
const scrape = [
{a: 1, b: 1},
{a: 5, b:5}
]
// create hash map using built-in Javascript Map
const pair = old.map(x => [JSON.stringify(x), true])
const map = new Map(pair)
// filter those object does not exist in hash map
const good = scrape.filter(x => !map.has(JSON.stringify(x)))
console.log(good)
How about something like this?
const good = scrape.filter((sEl) => {
return !old.some(oEl => oEl.a === sEl.a);
})
This avoids the nested forEach loops and .some will return as soon as a single true condition is found, avoiding some excess searching when an element exists early in the 'old' array.
May be something like:
var old = [
{a: 6, b: 3},
{a: 1, b: 1},
{a: 3, b: 3}
]
var scrape = [
{a: 1, b: 1},
{a: 5, b:5}
]
var result = scrape.filter(s => old.findIndex(o => o.a === s.a) === -1);
console.log(result);
I'd like to find best practice for getting the same section of two objects
const firstObject = { a: 1, b: 2, c: 3}
const secondObject = { 1, 2 }
// desired result: { a: 1, b: 2} or simply { a, b }
In my opinion, we need to do three steps:
1) Get value all values of each object
Object.values = Object.values || (obj => Object.keys(obj).map(key => obj[key]))
2) Find the same section from two arrays value
3) Find key-value pair from firstObject
Any other ways to do it?
Using Standard built-in objects as Array or Object is preferable
Break the firstObject into [key, value] pairs using Object#entries (or polyfill), and use Array#reduce to combine all those that exist in the secondObject.
const firstObject = { a: 1, b: 2, c: 3}
const secondObject = { 1: 1, 2: 2 };
const result = Object.entries(firstObject).reduce((obj, [key, value]) => {
value in secondObject && (obj[key] = value);
return obj;
}, {})
console.log(result);
Object.keys(firstObject).filter(key=> key in secondObject).reduce((o,k)=>(o[k]=firstObject[k],o),{});
Would like to merge an array of objects resulting in an object of unique keys and array of values (duplication of values is ok). Solutions in vanilla JS or lodash preferred.
eg - from this:
[{
a: 1,
b: 2
}, {
a: 1,
c: 3
}]
to this:
{
a: [1, 1],
b: [2],
c: [3]
}
You can use _.mergeWith() with the spread syntax to combine the objects:
const data = [{"a":1,"b":2},{"a":1,"c":3}];
const result = _.mergeWith({}, ...data, (v1 = [], v2) => [...v1, v2]);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
ES6 variant:
const a = [{
a: 1,
b: 2
}, {
a: 1,
c: 3
}]
const b = a.reduce((acc, cur) => Object.assign(acc,
...Object.keys(cur).map(key => ({ [key]: (acc[key] || []).concat(cur[key]) })))
, {})
console.log(b)
without loadash:
var t = [{
a: 1,
b: 2
}, {
a: 1,
c: 3
}];
var result = {};
debugger;
for(var i=0; i<t.length; i++){
for(var j in t[i]){
if(result.hasOwnProperty(j)){
result[j].push(t[i][j]);
}else{
result[j] = [t[i][j]];
}
}
}
console.log(result);
A quick search here in stack reveals that #elclanrs already wrote code for that here However based on the comments, it needs a little tweaking to accept an array of objects, so I added a bit of change to the original code itself.
so basically it boils to the function call:
var merge = function() {
return [].reduce.call(arguments, function(acc, x) {
for(i=0;i<x.length;i++){
Object.keys(x[i]).forEach(function(k) {
acc[k] = (acc[k]||[]).concat([x[i][k]])
});
}
return acc
},{})
}
}
Here's a snippet using the function call (with a bit of small change I put) in that post:
var x = [{a: 1, b: 2}, {a: 1,c: 3}]
var merge = function() {
return [].reduce.call(arguments, function(acc, x) {
for(i=0;i<x.length;i++){
Object.keys(x[i]).forEach(function(k) {
acc[k] = (acc[k]||[]).concat([x[i][k]])
});
}
return acc
},{})
}
y = merge(x);
alert(JSON.stringify(y));
You can use lodash#mergeWith wrapped in a lodash#spread to make lodash#mergeWith treat an array as a list of arguments. We use lodash#concat as a supporting function to concatenate an empty object (to avoid mutating the objects in the collection), the collection, and the customizer function that merges the entire collection. The customizer is composed using lodash#flow, wherein its first argument is lodash#concat that only accepts an arity of 2 using lodash#ary and the second argument uses lodash#compact -- It removes all undefined values in an array.
var result = _.spread(_.mergeWith)(
_.concat({}, data, _.flow(_.ary(_.concat, 2), _.compact))
);
var data = [{
"a": 1,
"b": 2
}, {
"a": 1,
"c": 3
}];
var result = _.spread(_.mergeWith)(
_.concat({}, data, _.flow(_.ary(_.concat, 2), _.compact))
);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>