Group multiple properties with underscore - javascript

I want to group and count the array below by id and name
var arr = [
{
id: 'A1',
name: 'andersson'
},
{
id: 'A1',
name: 'johansson'
},
{
id: 'A1',
name: 'andersson'
},
];
Expected output:
var aggregated = [
{
id: 'A1',
name: 'andersson',
count: 2
},
{
id: 'A1',
name: 'johansson',
count: 1
}
];
This should be a basic task but I cant find any handy way to do it with underscore. The _.groupBy function should probably be used somehow.
My dataset is big so performance is important.

You could first groupBy id and count and then use map to count each value.
var arr = [{
id: 'A1',
name: 'andersson'
}, {
id: 'A1',
name: 'johansson'
}, {
id: 'A1',
name: 'andersson'
}];
var result = _.chain(arr)
.groupBy(e => e.id + '|' + e.name)
.map(e => (e[0].count = e.length, e[0]))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Related

How to splice multiple values from array of objects which matches given nested array

I am trying to remove the array of objects if the given array of objects matches with the index but it is only removing the last index value.
How we can remove multiple values?
let idArr = [[{ index: 2 }], [{ index: 3 }]];
let obj = [
{
id: 1,
name: 'abc',
},
{
id: 2,
name: 'abc',
},
{
id: 3,
name: 'abc',
},
{
id: 4,
name: 'abc',
},
];
let data = obj.filter((item, i) =>
idArr.reduce((val) => val.find(({ index }) => i === index))
);
//expected output
[
{
id: 1,
name: 'abc',
},
{
id: 2,
name: 'abc',
},
];
I think that following code achieves what you're expecting
let data = obj.filter((obj, idx) => !idArr.find(id => id[0].index === idx));

Lodash group by specific words/prefix in a key

I wonder how I can group this array based on the prefix text in name key (split the name key at the : colon) using Lodash.
const tags = [
{ name: 'Animals: Frogs', id: 1 },
{ name: 'Animals: Lions', id: 2 },
{ name: 'Birds: Crows', id: 3 }
];
to
const tags = [{
animals: [
{ name: 'Frogs', id: 1 },
{ name: 'Lions', id: 2 },
],
birds: [
{ name: 'Crows', id: 3}
]
}];
Does Lodash have any functions to handle this, or is a custom function/regex needed?
If the pure JS suffices, it can be done this way (the result is an object here, not an array, but this can be changed if needed):
const tags = [
{ name: 'Animals: Frogs', id: 1 },
{ name: 'Animals: Lions', id: 2 },
{ name: 'Birds: Crows', id: 3 }
];
const tags2 = tags.reduce(
(acc, { name, id }) => {
let [group, type] = name.split(': ');
group = group.toLowerCase();
acc[group] ??= [];
acc[group].push({ name: type, id });
return acc;
},
{},
);
console.log(tags2);

Move items to front of array if they exists in another array, based on key

I'm sort an array based on the keys in another array. If they find a match, it would move those items to the front of the array. But I can't think of a clean way to do this.
let myArray = [
{ id: 'a', name: 'Mal' },
{ id: 'b', name: 'Wash'},
{ id: 'c', name: 'Inara'},
{ id: 'd', name: 'Jayne'},
]
let sortArray = [
{ id: 'b' },
{ id: 'c' },
{ id: 'x' },
]
/* Expected result
myArray = [
{ id: 'b', name: 'Wash'},
{ id: 'c', name: 'Inara'},
{ id: 'a', name: 'Mal' },
{ id: 'd', name: 'Jayne'},
]
/*
Does anyone know a way to do this without just looping through it a bunch of times? Thanks
You could create a Map which maps each id in sortArray to its index. Use this priority map object to sort the first array.
const array = [{ id: 'a', name: 'Mal' }, { id: 'b', name: 'Wash'}, { id: 'c', name: 'Inara'}, { id: 'd', name: 'Jayne'}],
sortArray = [{ id: 'b' }, { id: 'c' }, { id: 'x' }],
map = new Map( sortArray.map((o, i) => [o.id, i]) )
array.sort((a,b) =>
( map.has(b.id) - map.has(a.id) ) || ( map.get(a.id) - map.get(b.id) )
)
console.log(array)
You could take an object ffor the wanted order of the items and a default value for unknown items.
let array = [{ id: 'a', name: 'Mal' }, { id: 'b', name: 'Wash'}, { id: 'c', name: 'Inara'}, { id: 'd', name: 'Jayne'}],
sortArray = [{ id: 'b' }, { id: 'c' }, { id: 'x' }],
order = Object.fromEntries(sortArray.map(({ id }, i) => [id, i + 1]));
array.sort(({ id: a }, { id: b }) =>
(order[a] || Number.MAX_VALUE) - (order[b] || Number.MAX_VALUE)
);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Lodash - Sort by position in array

I have an array of objects
let myArray = [
{
id: 'first',
name: 'john',
},
{
id: 'second',
name: 'Emmy',
},
{
id: 'third',
name: 'Lazarus',
}
]
and an array
let sorter = ['second', 'third', 'first']
I would like to use lodash sorting method to sort my objects according to their position in sorter.
So that the output would be
let mySortedArray = [
{
id: 'second',
name: 'Emmy',
},
{
id: 'third',
name: 'Lazarus',
},
{
id: 'first',
name: 'john',
}
]
Is it possible to do so?
You can achieve this by using map and find:
let myArray = [
{
id: "first",
name: "john"
},
{
id: "second",
name: "Emmy"
},
{
id: "third",
name: "Lazarus"
}
];
let sorter = ["second", "third", "first"];
let mySortedArray = sorter.map(x => myArray.find(y => y.id === x));
console.log(mySortedArray);
Using lodash you can use _.sortBy
let myArray = [
{
id: 'first',
name: 'john',
},
{
id: 'second',
name: 'Emmy',
},
{
id: 'third',
name: 'Lazarus',
}
]
let sorter = ['second', 'third', 'first']
console.log(_.sortBy(myArray,(i) => {return sorter.indexOf(i.id)}))
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
If you want to sort the array in-place, you don't need Lodash, you can easily do it with vanilla JavaScript
let myArray = [
{
id: 'first',
name: 'john',
},
{
id: 'second',
name: 'Emmy',
},
{
id: 'third',
name: 'Lazarus',
}
]
let sorter = ['second', 'third', 'first']
//create a lookup table (map) to save looking through the array
const sortLookup = new Map();
//populate with element as key - index as value
sorter.forEach((id, index) => sortLookup.set(id, index));
//sort using the indexes of sorter
myArray.sort((a, b) => sortLookup.get(a.id) - sortLookup.get(b.id))
console.log(myArray)
This is using a Map but the same can easily be accomplished with a plain JavaScript Object {}. You don't even need to pre-compute the lookup myArray.sort((a, b) => sorter.indexOf(a.id) - sorter.indexOf(b.id)) would give the exact same output but it would mean that instead of traversing sorter once for a complexity of O(n), you potentially have O(n^m)or O(n^n) (if both arrays are the same length)
Since you have an index array in the case of the sorter you can _.keyBy the main array and then use the sorter to access by index:
let myArray = [ { id: 'first', name: 'john', }, { id: 'second', name: 'Emmy', }, { id: 'third', name: 'Lazarus', } ]
let sorter = ['second', 'third', 'first']
const idMap = _.keyBy(myArray, 'id')
const result = _.map(sorter, x => idMap[x])
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
This should perform better since you only do the idMap once and then access it by index.

Get property of first objects in two arrays

I have two array of objects:
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' },
];
I need to create an array of strings that contains:
1. The Name of the first Book in books (if there are any)
2. The Names of the first 2 Cars in cars (if there are any)
I can do:
if (books.length > 0)
var bookA = books[0].name;
or:
if (cars.length > 1) {
var carA = cars[0].name;
var carB = cars[1].name;
}
Then build the string array but I believe there might be a better way to do this.
Can use filter() and map()
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' }
];
var res = [books[0], cars[0], cars[1]]
.filter(e => e)// remove undefined's
.map(({name:n}) => n)
console.log(res)
If you are using ES6. You can use [...array1,...array2] to merge them. So I slice the first item in book and use map to get a new array with only string name, and map it to result array.
For the cars array I slice the first two cars and do the same
var resultArray =[];
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' }
];
resultArray = [...resultArray, ...books.slice(0,1).map(v => v.name)]
resultArray = [...resultArray, ...cars.slice(0,2).map(v => v.name)]
console.log(resultArray)
One of a million ways to do it. This one would allow you to easily create a data structure (arrayDefinition) that configures what property to get from which array and at which index, which you could e.g. retrieve from a RESTful webservice.
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' },
];
const arrayDefinition = [
{
source: 'books',
index: 0,
prop: 'name'
},
{
source: 'cars',
index: 0,
prop: 'name'
},
{
source: 'cars',
index: 1,
prop: 'name'
}
];
let resultArr = []
arrayDefinition.forEach(function(def) {
if (Array.isArray(window[def.source]) && window[def.source].length >= def.index) {
resultArr.push(window[def.source][def.index][def.prop])
}
})
console.log(resultArr)

Categories