Lodash orderBy object of arrays of objects - javascript

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>

Related

Count the number of occurrences and add it to grouped objects in a JSON array

Here's my input value -
var input = [
{
status: 'yes',
data: ['a', 'b', 'c', 'd'],
score: 2,
rank: 2,
},
{
status: 'yes',
data: ['a', 'b', 'c'],
score: 9,
rank: 2,
},
{
status: 'yes',
data: ['a', 'b', 'c'],
score: 8,
rank: 2,
},
{
status: 'no',
data: ['a', 'b', 'c', 'd'],
score: 12,
rank: 3,
},
{
status: 'no',
data: ['a', 'b', 'c'],
score: 9,
rank: 3,
},
{
status: 'no',
data: ['a', 'b', 'c', 'd'],
score: 5,
rank: 3,
},
]
And here's what I'm trying to get as an output value -
[
{
status: 'yes',
data: ['a', 'b', 'c', 'd'],
occurrence: 1,
rank: 2,
},
{
status: 'yes',
data: ['a', 'b', 'c'],
occurrence: 2,
rank: 2,
},
{
status: 'no',
data: ['a', 'b', 'c', 'd'],
occurrence: 2,
rank: 3,
},
{
status: 'no',
data: ['a', 'b', 'c'],
occurrence: 1,
rank: 3,
},
]
The idea is to -
Remove the score parameter from all the objects
Add the occurrence parameter to all the objects
Assign the "occurrence" value i.e. the number of times we see the same data being repeated as per each of the status
Here's the code that I'm using (adopted from the 2nd half of this solution) -
const res = Array.from(input.reduce((acc, {score, ...r}, index) => {
const key = JSON.stringify(r);
const current = acc.get(key) || {...r, occurrence: 0};
return acc.set(key, {...current, occurrence: current.occurrence + index});
}, new Map).values());
console.log(res);
But that's producing an unexpected output that looks like this -
[
{
"status": "yes",
"data": ["a","b","c","d"],
"rank": 2,
"occurrence": 0
},
{
"status": "yes",
"data": ["a","b","c"],
"rank": 2,
"occurrence": 3
},
{
"status": "no",
"data": ["a","b","c","d"],
"rank": 3,
"occurrence": 8
},
{
"status": "no",
"data": ["a","b","c"],
"rank": 3,
"occurrence": 4
}
]
There's something that I'm missing to get the correct occurrences & I can't for the life of me understand what.
I will do that this way
const
input =
[ { status: 'yes', data: [ 'a', 'b', 'c', 'd' ], score: 2, rank: 2 }
, { status: 'yes', data: [ 'a', 'b', 'c' ], score: 9, rank: 2 }
, { status: 'yes', data: [ 'a', 'b', 'c' ], score: 8, rank: 2 }
, { status: 'no', data: [ 'a', 'b', 'c', 'd' ], score: 12, rank: 3 }
, { status: 'no', data: [ 'a', 'b', 'c' ], score: 9, rank: 3 }
, { status: 'no', data: [ 'a', 'b', 'c', 'd' ], score: 5, rank: 3 }
]
, result = input.reduce((a,{status,data,rank}) =>
{
let same = a.find( x => x.status === status
&& x.rank === rank
&& JSON.stringify(x.data) === JSON.stringify(data)
)
if (!same)
a.push({ status, data: [...data], occurrence: 1, rank })
else
same.occurrence++
return a
},[])
console.log( result )
.as-console-wrapper {max-height: 100%!important;top:0}
I really didn't catch what did you want to do with occurence value. In your example you were showing:
status: 'no', data: ['a', 'b', 'c', 'd'], occurrence: 2,
And why is here occurence equals 2 whereas 'no' is the first status with this value?
At first try to remove current.occurrence + index and leave just current.occurrence if it will be always zero it will be correct to make next steps to calculate what you want.
I doubt that you need index there. Try to explain, and it helps to provide a decision.

Is there any way how to find elements of array by multiple values? [duplicate]

This question already has answers here:
Filter array of objects based on another array in javascript
(10 answers)
How to filter object array based on attributes?
(21 answers)
Closed 3 years ago.
I have an array:
people = [
{name: a, group: 1},
{name: b, group: 2},
{name: c, group: 3},
{name: d, group: 2},
{name: e, group: 3},
{name: f, group: 1},
{name: g, group: 1},
];
I need to find all people, who are in groups 2 and 3.
Desired output:
filteredPeople = [
{name: b, group: 2},
{name: c, group: 3},
{name: d, group: 2},
{name: e, group: 3},
];
It might be other groups as well (groups using for search may change).
How I have to do that?
You can use .filter() and .includes() methods to get the desired output:
const data = [
{name: 'a', group: 1}, {name: 'b', group: 2}, {name: 'c', group: 3},
{name: 'd', group: 2}, {name: 'e', group: 3}, {name: 'f', group: 1},
{name: 'g', group: 1}
];
const groups = [2, 3];
const result = data.filter(({ group }) => groups.includes(group));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
filter and includes do the job here. We use the rest operators (which gives us an array on which we can apply includes). So you can put any number of groups into the function call to be flexible when filtering.
let people = [{name: "a",group:1},{name: "b",group:2},{name: "c",group:3},{name:"d",group:2},{name:"e",group:3},{name:"f",group:1},{name:"g",group:1}];
let filterGroups = (arr, ...groups) => arr.filter(o => groups.includes(o.group));
console.log(filterGroups(people, 2, 3));
Using Array.prototype.filter() and the expression 2 === group || group === 3
const data = [{name: 'a', group: 1}, {name: 'b', group: 2}, {name: 'c', group: 3}, {name: 'd', group: 2}, {name: 'e', group: 3}, {name: 'f', group: 1}, {name: 'g', group: 1}];
const result = data.filter(({ group }) => group === 2 || group === 3);
console.log(result);

Convert array of objects to one Object using ramda.js

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>

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>');

Categories

Resources