GroupBy items Using Underscore based on two keys - javascript

I've one object, which I want to be group by based on two keys.
var obj = [{
make: "nissan",
model: "sunny",
colour: "red"
},
{
make: "nissan",
model: "sunny",
colour: "red"
},
{
make: "nissan",
model: "sunny",
colour: "red1"
}];
var result = _.groupBy(obj, p=>p.model);
gives me one result.
I want this to be group Base on model and color, so that I've two results as:
result = [{
make: "nissan",
model: "sunny",
colour: "red"
},
{
make: "nissan",
model: "sunny",
colour: "red"
}];
How I can do this with the help of Underscore js or any other short way.

With underscore.js groupBy you can group multiple properties like this:
const obj = [{make: "nissan",model: "sunny",colour: "red"}, {make: "nissan",model: "sunny",colour: "red"},{make: "nissan",model: "sunny",colour: "red1"}];
const result = _.groupBy(obj, item => item.model + '#' + item.colour);
console.log(result);
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
But the result you show in your question looks like you need is Array.prototype.filter():
const obj = [{make: "nissan",model: "sunny",colour: "red"}, {make: "nissan",model: "sunny",colour: "red"},{make: "nissan",model: "sunny",colour: "red1"}];
const result = obj.filter(item => item.model === 'sunny' && item.colour === 'red');
console.log(result);

since you asked "How I can do this with the help of Underscore js or any other short way" , here is a short way using Array.prototype.filter()
var obj = [{
make: "nissan",
model: "sunny",
colour: "red"
},{
make: "nissan",
model: "sunny",
colour: "red"
},
{
make: "nissan",
model: "sunny",
colour: "red1"
}];
var res = obj.filter( key => key.colour === "red")
console.log(res)

var result = _.groupBy(obj, function(o){
return o.model + o.color;
});

Related

Create sub array with key value and sort it

Given below is an array of objects, need to create sub-array in it with common make key, also all sub-array and main array needs to be sorted as follows:
the main array to be sorted on make key alphabetically.
sub-arrays to be sorted ASC on the basis of the year.
Input Array:
const cars = [
{
"make": "audi",
"model": "r8",
"year": "2006"
}, {
"make": "audi",
"model": "s5",
"year": "2005"
}, {
"make": "ford",
"model": "mustang",
"year": "2012"
}, {
"make": "ford",
"model": "fusion",
"year": "2015"
}, {
"make": "kia",
"model": "optima",
"year": "2012"
},
];
Desired Array:
const cars = [
{
"make": "audi",
"list": [
{
"model": "s5",
"year": "2005",
},
{
"model": "r8",
"year": "2006",
},
],
},
{
"make": "ford",
"list": [
{
"model": "mustang",
"year": "2012",
},
{
"model": "fusion",
"year": "2015",
},
],
},
{
"make": "kia",
"list": [
{
"model": "optima",
"year": "2012",
},
],
},
];
What I have tried yet:
let filteredData = [];
cars.forEach((value)=>{
var foundIndex = filteredData.findIndex( car => car.make === value.make );
if(foundIndex != -1){
let carData = filteredData[foundIndex];
const {list} = carData;
let nextObj = {'model': value.model, 'year': value.year};
const newData = [...list, nextObj];
carData['list'] = newData;
filteredData[foundIndex] = carData;
}else{
let values = {'model': value.model, 'year': value.year};
let data = [values];
let obj = {'make':value.make, 'list': data};
filteredData.push(obj);
}
});
const sortedCars = filteredData.sort((a, b) => a.make.localeCompare(b.make));
Any optimal solution for this to make it better is welcomed.
This is really just a 'group by' with sorting. To avoid having to sort multiple nested arrays in the result you can sort (a copy) of the array by year before grouping, and then sort the result by make afterwards. Here using a for...of loop grouping into an object and then sorting the Object.values() as the result.
const cars = [
{ make: 'ford', model: 'fusion', year: '2015' },
{ make: 'audi', model: 'r8', year: '2006' },
{ make: 'audi', model: 's5', year: '2005' },
{ make: 'ford', model: 'mustang', year: '2012' },
{ make: 'kia', model: 'optima', year: '2012' },
];
const grouped = {};
for (const { make, ...rest } of [...cars].sort((a, b) => a.year - b.year)) {
(grouped[make] ??= { make, list: [] }).list.push({ ...rest });
}
const result = Object.values(grouped).sort((a, b) =>
a.make.localeCompare(b.make)
);
console.log(result);
Alternatively, because javascript objects sort integer properties by default you can 'group by' year within each list and then map the resulting object values to arrays. The outer array will need to be sorted explicitly.
const cars = [
{ make: 'ford', model: 'fusion', year: '2015' },
{ make: 'audi', model: 'r8', year: '2006' },
{ make: 'audi', model: 's5', year: '2005' },
{ make: 'ford', model: 'mustang', year: '2012' },
{ make: 'kia', model: 'optima', year: '2012' },
];
const result = Object.values(
cars.reduce((a, { make, year, ...rest }) => {
a[make] ??= { make, list: {} };
a[make].list[year] ??= [];
a[make].list[year].push({ year, ...rest });
return a;
}, {}))
.map(({ make, list }) => ({ make, list: Object.values(list).flat() }))
.sort((a, b) => a.make.localeCompare(b.make));
console.log(result);
let filteredData = [];
cars.forEach((value)=>{
var foundIndex = filteredData.findIndex( car => car.make === value.make );
if(foundIndex != -1){
let carData = filteredData[foundIndex];
const {list} = carData;
let nextObj = {'model': value.model, 'year': value.year};
let newData = [...list, nextObj];
newData.sort((a, b) => a.year.localeCompare(b.year));
carData['list'] = newData;
filteredData[foundIndex] = carData;
}else{
let values = {'model': value.model, 'year': value.year};
let data = [values];
let obj = {'make':value.make, 'list': data};
filteredData.push(obj);
}
});
const sortedCars = filteredData.sort((a, b) => a.make.localeCompare(b.make));

How to convert array to object array with custom key in typescript?

I am learning typescript. It might be a silly question, but I am not able to find the answer online. I would like to convert an array to an object array (values are from the array). For example:
Input:
const array = ["Tom", "Jack", "Rose"]
Expected ouput:
[
{
name: "Tom",
initial: "t",
year: "2021"
},
{
name: "Jack",
initial: "j",
year: "2021"
},
{
name: "Rose",
initial: "r",
year: "2021"
},
]
What is the best way to achieve this in typescript?
Thanks!
This maybe the easiest way:
const array = ["Tom", "Jack", "C"]
const newObj = [];
array.forEach(eachArrayElement => {
const x = {
name: eachArrayElement,
initial: eachArrayElement[0].toLowerCase(),
year: (new Date().getFullYear()).toString()
};
newObj.push(x);
})
console.log('New Obj ==>', newObj);
Maybe even easier:
const array = ['Tom', 'Jack', 'Rose'];
const arrayOfObjects = array.map(element => {
return {
name: element,
initial: element.charAt(0),
year: new Date().getFullYear().toString(),
};
});
console.log(arrayOfObjects);

How to output everything from the object to the markup?

I have an array which contains objects. How to output everything from the object to the markup?
I think I stuck in this part of code div_child.innerHTML
On output I got this cars[object Object] eleven times.
Should be like this:
img
car: 'Audi'
model: 'A6'
age: 2014
price: 20900
let cars = [];
cars[0] = {
img: 'assets/img/img-1.webp',
car: 'Audi',
model: 'A6',
age: 2014,
price: 20900
}
// and 9 another objects of cars
let div = document.createElement('div');
div.className = 'cars-list';
for (let key in cars) {
let div_child = document.createElement('div');
div_child.className = 'cars-list__item';
div_child.innerHTML = `${cars}` + `${cars[key]}`; // As I understood this is the root of problem
div.append(div_child);
}
document.body.append(div);
Why is using "for...in" for array iteration a bad idea?
I would use a map:
let cars = [{
img: 'assets/img/img-1.webp',
car: 'Audi',
model: 'A4',
age: 2014,
price: 20900
},{
img: 'assets/img/img-2.webp',
car: 'Audi',
model: 'A5',
age: 2014,
price: 20900
},{
img: 'assets/img/img-3.webp',
car: 'Audi',
model: 'A6',
age: 2014,
price: 20900
},
]
// and 9 another objects of cars
let div = document.createElement('div');
div.className = 'cars-list';
div.innerHTML = cars.map(car => `<div class="cars-list__item">${car.car}: ${car.model}</div>`).join("")
document.body.append(div)

Reduce array of objects to unique keys and add array of values common to that key

I would like to group the values of this array by its car
key and then push the values common to that car into a values array.
I managed to do it with this but was wondering if there was an simpler way to do it with reduce.
const arr = [{
car: 'audi',
value: 'black'
}, {
car: 'audi',
value: 'expensive'
}, {
car: 'fiat',
value: 'red'
}, {
car: 'fiat',
value: 'cheap'
}]
// Simple array with unique car
const cars = Array.from(new Set(arr.map(({ car }) => car)))
// Array of objects with unique `car` and an empty `values` array for each
const result = cars.map((car) => ({ car, values: [] }))
// Push to values array the `value` for each car
arr.map((obj) => {
result.map((res) => {
if (obj.car === res.car) {
res.values.push(obj.value)
}
})
})
console.log(result)
/*
[{
car: 'audi',
values: ['black', 'expensive']
}, {
car: 'fiat',
values: ['red', 'cheap']
}]
*/
Make an object indexed by the car name, then iterate over original array, pushing the value to the array on the object:
const arr = [{
car: 'audi',
value: 'black'
}, {
car: 'audi',
value: 'expensive'
}, {
car: 'fiat',
value: 'red'
}, {
car: 'fiat',
value: 'cheap'
}];
const carsByName = {};
for (const { car, value } of arr) {
if (!carsByName[car]) carsByName[car] = { car, value: [] };
carsByName[car].value.push(value);
}
console.log(Object.values(carsByName));
While this could be done with reduce, it's arguably not very semantically appropriate when the accumulator never changes (and is a bit noisy, syntactically):
const arr = [{
car: 'audi',
value: 'black'
}, {
car: 'audi',
value: 'expensive'
}, {
car: 'fiat',
value: 'red'
}, {
car: 'fiat',
value: 'cheap'
}];
const carsByName = arr.reduce((a, { car, value }) => {
if (!a[car]) a[car] = { car, value: [] };
a[car].value.push(value);
return a;
}, {});
console.log(Object.values(carsByName));
Just use reduce for it. Keep in acc the array of cars with keys car and values. And then map it. I mean:
const arr = [
{
car: "audi",
value: "black",
},
{
car: "audi",
value: "expensive",
},
{
car: "fiat",
value: "red",
},
{
car: "fiat",
value: "cheap",
},
];
const result = Object.entries(
arr.reduce((acc, { car, value }) => {
if (acc[car]) {
return {
...acc,
[car]: [...acc[car], value],
};
}
return { ...acc, [car]: [value] };
}, [])
).map(([car, values]) => ({ car, values }));
console.log(result);
You can use .reduce to group the items by car, and then, .map to create a list of objects having car and its values:
const arr = [{
car: 'audi',
value: 'black'
}, {
car: 'audi',
value: 'expensive'
}, {
car: 'fiat',
value: 'red'
}, {
car: 'fiat',
value: 'cheap'
}]
let result = arr.reduce((acc,item) => {
const values = acc[item.car];
acc[item.car] = values ? [...values, item.value] : [item.value];
return acc;
}, {});
result = Object.entries(result).map(([car, values]) => ({car,values}));
console.log(result)

Filter an array of objects based on filter criteria in another array of objects: JavaScript

I wish to filter myArray based on criteria mentioned in myFilter.
The keys of myFilter are defined and could be accessed using myFilter.field, myFilter.value where as key:value of myArray are unknown.
We might have to iterate over each object in myArray to first match the myArray [key] with myFilter.field and then to that myArray [key] to myFilter.value.
That should be an AND logic
myArray = [{
make: "Honda",
model: "CRV",
year: "2017"
},
{
make: "Toyota",
model: "Camry",
year: "2020"
},
{
make: "Chevy",
model: "Camaro",
year: "2020"
}
]
myFilter = [{
field: "make",
value: "Chevy",
type: "string"
},
{
field: "year",
value: "2020",
type: "date"
}
];
// Expected OutPut:
myArray = [{
make: "Chevy",
model: "Camaro",
year: "2020"
}]
var tempArray = [];
const keysToMatch = myFilter.length;
let matchedItems = [];
myArray.forEach((data) => {
matchedItems = [];
let itemsToFind = Object.values(data);
myFilter.forEach((filterItem) => {
if (itemsToFind.indexOf(filterItem.value) != -1) {
matchedItems.push("matched");
}
});
//check if everything matched
if (matchedItems.length === keysToMatch) {
tempArray.push(data);
}
});
console.log(tempArray);
var tempArray = [];
const keysToMatch = myFilter.length;
let matchedItems = [];
myArray.forEach((data) => {
matchedItems = [];
let itemsToFind = Object.values(data);
myFilter.forEach((filterItem) => {
if (itemsToFind.indexOf(filterItem.value) != -1) {
matchedItems.push("matched");
}
});
//check if everything matched
if (matchedItems.length === keysToMatch) {
tempArray.push(data);
}
});
console.log(tempArray);
You can use normal for loop and array filter. Define a variable filteredArray and use for loop to iterate myFilter. During each iteration create a variable k whose value will be set to the filtered array. So at the first step the initial value of k will be the myArray and k will be filtered and the filtered value will be set to filteredArray. During second iteration and so on the value of k will be set to the first filtered array
let myArray = [{
make: "Honda",
model: "CRV",
year: "2017"
},
{
make: "Toyota",
model: "Camry",
year: "2020"
},
{
make: "Chevy",
model: "Camaro",
year: "2020"
}
]
let myFilter = [{
field: "make",
value: "Chevy",
type: "string"
},
{
field: "year",
value: "2020",
type: "date"
}
];
let filteredArray;
for (let i = 0; i < myFilter.length; i++) {
let k = filteredArray !== undefined ? filteredArray : myArray
if (myFilter[i].field === 'make') {
filteredArray = k.filter(item => item[myFilter[i].field] === myFilter[i].value)
} else if (myFilter[i].field === 'year') {
filteredArray = k.filter(item => item[myFilter[i].field] === myFilter[i].value)
}
}
console.log(filteredArray)
This should theoretically work, but for some reason doesn't (returns an empty array). I'm hoping for some other reader's heads up to make it work! Feel free to edit.
myArray = [{
make: "Honda",
model: "CRV",
year: "2017"
},
{
make: "Toyota",
model: "Camry",
year: "2020"
},
{
make: "Chevy",
model: "Camaro",
year: "2020"
}
]
myFilter = [{
field: "make",
value: "Chevy",
type: "string"
},
{
field: "year",
value: "2020",
type: "date"
}
];
const myNewArray = myArray.filter((car) => myFilter.every((filter) => car[filter.field] === car[filter.value]));
console.log(myNewArray);
This might be a bit over complicated for what you need, but it works.
myArray = [
{
make: "Honda",
model: "CRV",
year: "2017"
},
{
make: "Toyota",
model: "Camry",
year: "2020"},
{
make: "Chevy",
model: "Camaro",
year: "2020"}
]
myFilter = [
{
field: "make",
value: "Chevy",
type: "string"
},
{
field: "year",
value: "2020",
type: "date"
}
];
//only return those that return true
var newArray = myArray.filter(car => {
var temp = true;
//iterate over your filters
for (var i = 0; i < myFilter.length; i++) {
//if any filters result in false, then temp will be false
if (car[myFilter[i].field] != myFilter[i].value) {
temp = false;
}
}
if (temp == true) {
return true;
} else {
return false;
}
});
console.log(JSON.stringify(newArray));

Categories

Resources