Convert array of objects to one Object using ramda.js - javascript

I have an array:
var a = [
{id: 1, val: 'a'},
{id: 2, val: 'b'},
{id: 3, val: 'c'},
{id: 4, val: 'd'},
]
And I want to get transform it to:
var b = {
1: 'a',
2: 'b',
3: 'c',
4: 'd',
}
Actually I'm using pure js:
var b = a.reduce(
(ac, pr) => ({
...ac,
[pr.id]: pr.val,
}),
{}
);
But maybe Ramda.js have something special for that purpose?

You are looking for Ramda's .mergeAll() method:
var b = R.mergeAll(a.map(function(o) {
return {
[o.id]: o.val
}
}));
The .map()call will return the custom object from each item, taking only the values, then .mergeAll() will merge the array into one object.
mergeAll Documentation:
Merges a list of objects together into one object.
Demo:
var a = [{
id: 1,
val: 'a'
},
{
id: 2,
val: 'b'
},
{
id: 3,
val: 'c'
},
{
id: 4,
val: 'd'
},
];
var b = R.mergeAll(a.map(function(o) {
return {
[o.id]: o.val
}
}));
console.log(b);
<script src="https://cdn.jsdelivr.net/ramda/0.18.0/ramda.min.js"></script>

If anyone still passes by here, it does indeed:
R.indexBy(R.prop('id'), someArray);
See indexBy in Ramda's documentation
EDIT:
Bennet is correct. If we want val as the only value per key, we can "pluck" it out after:
const createValDict = R.pipe(
R.indexBy(R.prop('id')),
R.pluck('val')
)
const valDict = createValDict(myArr)
Pluck works on objects too

Get the ordered values from each object by mapping with R.props, and use R.fromPairs to create an object:
var a = [
{id: 1, val: 'a'},
{id: 2, val: 'b'},
{id: 3, val: 'c'},
{id: 4, val: 'd'},
];
var result = R.compose(R.fromPairs, R.map(R.props(['id', 'val'])));
console.log(result(a));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

With plain Javascript, you could use a combination with Object.assign, spread syntax ..., Array#map, destructuring assignment and short hand properties.
var a = [{ id: 1, val: 'a' }, { id: 2, val: 'b' }, { id: 3, val: 'c' }, { id: 4, val: 'd' }],
result = Object.assign(...a.map(({ id, val }) => ({ [id]: val })));
console.log(result);

var a = [
{id: 1, val: 'a'},
{id: 2, val: 'b'},
{id: 3, val: 'c'},
{id: 4, val: 'd'},
]
var result = {};
for (var i=0; i<a.length; i++) {
result[a[i].id] = a[i].val;
}
console.log(result);

If you wanted something point-free, you could write:
const combine = compose(mergeAll, map(lift(objOf)(prop('id'), prop('val'))))
const {compose, mergeAll, map, lift, objOf, prop} = R;
const combine = compose(mergeAll, map(lift(objOf)(prop('id'), prop('val'))))
var a = [{id:1, val:'a'}, {id:2, val:'b'}, {id:3, val:'c'}, {id:4, val:'d'}]
console.log(combine(a));
<script src="https://cdn.jsdelivr.net/ramda/0.18.0/ramda.min.js"></script>

Here it works like a charm :
var a = [
{id: 1, val: 'a'},
{id: 2, val: 'b'},
{id: 3, val: 'c'},
{id: 4, val: 'd'},
];
// var b = R.fromPairs( a.map(Object.values) );
// Perhaps this is the more general and order independent way:
var b = R.fromPairs(a.map( ({id,val})=>[id,val] ));
console.log( b );
<script src="//cdn.jsdelivr.net/npm/ramda#latest/dist/ramda.min.js"></script>

This might be the simplest way:
pipe(map(props(['id', 'val'])), fromPairs)(a)
#spflow's answer is simpler but not guaranteed to work on all platforms. Ramda code golf is always fun!
const { fromPairs, map, pipe, props } = R
const a = [
{id: 1, val: 'a'},
{id: 2, val: 'b'},
{id: 3, val: 'c'},
{id: 4, val: 'd'},
]
const result = pipe(map(props(['id', 'val'])), fromPairs)(a)
console.log(result)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.min.js"></script>

Yet one approach:
const { indexBy, prop, pipe, pluck } = R
const a = [
{id: 1, val: 'a'},
{id: 2, val: 'b'},
{id: 3, val: 'c'},
{id: 4, val: 'd'},
]
const result = pipe(indexBy(prop('id')), pluck('val'))(a)
console.log(result)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.min.js"></script>

Simplest, point-free:
compose(fromPairs, map(values))(a)
const { compose, fromPairs, map, values } = R
const a = [
{id: 1, val: 'a'},
{id: 2, val: 'b'},
{id: 3, val: 'c'},
{id: 4, val: 'd'},
]
const result = compose(fromPairs, map(values))(a)
console.log(result)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Related

finding index of item in nested array

If I have an array (sub) which has its own objects each with arrays within them and I'm looking for a particular value such as id === 9, how would I find the index of the object AND the index within that object's s array?
let a = {
sub: [
{
id: 1,
s: [
{id: 5},
{id : 1}
]
},
{
id: 2,
s: [
{id: 6},
{id: 3}
]
},
{
id: 3,
s: [
{id: 9},
{id: 2}
]
}
]
}
console.log(a.sub.findIndex(a => a.s.findIndex(z => z.id === 9)))
If you're sure there's only one matching element in all your sub arrays, here's a little trick with flatMap.
let a = {
sub: [
{
id: 1,
s: [
{id: 5},
{id: 1}
]
},
{
id: 2,
s: [
{id: 6},
{id: 3}
]
},
{
id: 3,
s: [
{id: 9},
{id: 2}
]
}
]
}
console.log(a.sub.flatMap((a, i) => {
const j = a.s.findIndex(z => z.id === 9);
return j > -1 ? [i, j] : []
}));
This will return an array containing the index, i, in a.sub where a matching element is found followed by the index, j, in a.sub[i].s where the matching element was found.
Note flatMap is a relatively recent addition to the standard, so it may not work in older browsers. Be sure to use a polyfill or a transpiler like Babel, if this is a concern in your case.
Try this:
let a = {
sub: [
{
id: 1,
s: [
{id: 5},
{id : 1}
]
},
{
id: 2,
s: [
{id: 6},
{id: 3}
]
},
{
id: 3,
s: [
{id: 9},
{id: 2}
]
}
]
}
v = 9
id1 = a.sub.findIndex(e => e.s.findIndex(ee => ee.id === v)!= -1)
id2 = a.sub[id1].s.findIndex(e => e.id === v )
console.log(id1) //index of the object
console.log(id2) //index within that object's s array
Modified answer of p.s.w.g, less likely to give you an eslint error.
let a = {
sub: [
{
id: 1,
s: [
{id: 5},
{id: 1}
]
},
{
id: 2,
s: [
{id: 6},
{id: 3}
]
},
{
id: 3,
s: [
{id: 9},
{id: 2}
]
}
]
}
console.log(a.sub.flatMap((a, i) => {
const j = a.s.findIndex(z => z['id'] === 9);
return j > -1 ? [i, j] : []
}));

Lodash orderBy object of arrays of objects

I have an array:
const names = [{name: 'a', equal: 3},
{name: 'b', equal: 2},
{name: 'c', equal: 2},
{name: 'd', equal: 3},
{name: 'a1', equal: 1}]
I'm running _.groupBy(names, 'equal') and as a result I get:
{1: [{equal: 1, name: 'a1'}],
2: [{equal: 2, name: 'b'}, {equal: 2, name: 'c'}],
3: [{equal: 3, name: 'a'}, {equal: 3, name: 'd'}]}
But here the sorting by name disappears. How do I sort by name in this object?
Update
I want to get:
{3: [{equal: 3, name: 'a'}, {equal: 3, name: 'd'}],
1: [{equal: 1, name: 'a1'}],
2: [{equal: 2, name: 'b'}, {equal: 2, name: 'c'}]}
By using an array as result set, you could sort it later after grouping.
var names = [{ name: 'a', equal: 3 }, { name: 'b', equal: 2 }, { name: 'c', equal: 2 }, { name: 'd', equal: 3 }, { name: 'a1', equal: 1 }],
equals = {},
result = [];
names.forEach(function (o) {
if (!equals[o.equal]) {
equals[o.equal] = [];
result.push(equals[o.equal]);
}
equals[o.equal].push(o);
});
result.sort(function (a, b) {
return a[0].name.localeCompare(b[0].name);
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use lodash methods groupBy and sortBy together
DEMO
const names = [{name: 'a', equal: 3},
{name: 'b', equal: 2},
{name: 'c', equal: 2},
{name: 'd', equal: 3},
{name: 'a1', equal: 1}];
var grouped = _.groupBy(names, function(name) {
return name.equal;
});
var result = _.sortBy(grouped, ['name']);
console.log(grouped);
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>

Is there any lodash function to achieve this?

I have an array
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
]
I need output like this:
[{id: 1, items: [3, 4, 5]}, {id: 2, items: [6,7]}, {id: 3, items: [8]}]
Here's a solution that first groups by id and then maps across the groupings to get the required collection:
let result = _(a)
.groupBy('id')
.map( (group ,id) => ({id: id, items: _.map(group, 'item')}))
.value()
It's pretty ugly, but then other answers are not pretty either
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
];
var ret = _.chain(a)
.groupBy(elt => elt.id)
.mapValues(elt => _.reduce(elt, (acc, sub) => acc.concat(sub.item),[]))
.map((value, key) => ({id: key, items:value}))
.value();
console.log(ret);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

Ramda: get objects from array by comparing with each item in another array

I've an array like:
ids = [1,3,5];
and another array like:
items: [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
{id: 5, name: 'e'},
{id: 6, name: 'f'}
];
What I want is another array like:
array = [{id: 1, name: 'a'}, {id: 3, name: 'c'}, {id: 5, name: 'e'}];
I can't get my head around it. so far i tried like:
console.log(R.filter(R.propEq('id', <donnow what shud be here>), items);
console.log( R.pick(ids)(items))
If you still want to do with Ramda:
const ids = [1,3,5];
const items = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
{id: 5, name: 'e'},
{id: 6, name: 'f'}
];
console.log(
R.filter(R.compose(R.flip(R.contains)(ids), R.prop('id')), items)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
You can use .filter and .indexOf. Note these are ECMA5 methods for Arrays, and will not work in IE8.
var ids = [1, 3, 5];
var items = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
{id: 5, name: 'e'},
{id: 6, name: 'f'}
];
var filtered = items.filter(function(obj) {
return ids.indexOf(obj.id) > -1;
});
console.log(filtered); // [{id: 1, name: 'a'}, {id: 3, name: 'c'}, {id: 5, name: 'e'}];
Or may be one liner without Ramda
items.filter(x=>ids.includes(x.id))
I suggest to use a hash table for faster lookup.
var ids = [1, 3, 5],
items = [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}, {id: 4, name: 'd'}, {id: 5, name: 'e'}, {id: 6, name: 'f'} ],
filtered = items.filter(function(obj) {
return this[obj.id];
}, ids.reduce(function (r, a) {
r[a] = true;
return r;
}, Object.create(null)));
document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');

Compare the elements of two arrays by Id and remove the elements from the one array that are not presented in the other

I have two arrays of objects like this:
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"}, {Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}]
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}]
I need to compare the elements of the two arrays by Id and remove the elements from arr1 that are not presented in arr2 ( does not have element with that Id). How can I do this ?
var res = arr1.filter(function(o) {
return arr2.some(function(o2) {
return o.Id === o2.Id;
})
});
shim, shim, shim.
You can use a function that accepts any number of arrays, and returns only the items that are present in all of them.
function compare() {
let arr = [...arguments];
return arr.shift().filter( y =>
arr.every( x => x.some( j => j.Id === y.Id) )
)
}
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"}, {Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}];
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}, {Id: 30, Name: "Test3"}];
var arr3 = [{Id: 1, Name: "Test1"}, {Id: 6, Name: "Test3"}, {Id: 30, Name: "Test3"}];
var new_arr = compare(arr1, arr2, arr3);
console.log(new_arr);
function compare() {
let arr = [...arguments]
return arr.shift().filter( y =>
arr.every( x => x.some( j => j.Id === y.Id) )
)
}
Making use of a hash (a Set) will give a performance gain:
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"},
{Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}];
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}];
arr1 = arr1.filter(function (el) {
return this.has(el.Id);
}, new Set(arr2.map(el => el.Id)));
console.log(arr1);
A new Set is created that gets the Id values from arr2:
"1","3"
That Set is passed as the thisArg to filter, so that within the filter callback it is available as this.

Categories

Resources