How can I achieve this with Lodash? - javascript

I would like to group all messages between 2 people in a group (chat). Doesn`t matter if I am the author or receiver.
Let's say this example code.
const messages = [
{ id: '100', text: 'aaa', author: { id: '1' }, receiver: { id: '2' } },
{ id: '101', text: 'bbb', author: { id: '2' }, receiver: { id: '1' } },
{ id: '102', text: 'ccc', author: { id: '3' }, receiver: { id: '1' } },
]
Imagine, I am user ID = 1, so I would like to get this:
const chats = [
{
chatName: 'Name of user ID 2', messages: [
{ id: '100', text: 'aaa', author: { id: '1' }, receiver: { id: '2' } },
{ id: '101', text: 'bbb', author: { id: '2' }, receiver: { id: '1' } },
]
},
{
chatName: 'Name of user ID 3', messages: [
{ id: '102', text: 'ccc', author: { id: '3' }, receiver: { id: '1' } },
]
}
];
How can I achieve this with Lodash?

Not sure about lodash, but you can use plain js - reduce and map to get that structure
const messages = [{
id: '100',
text: 'aaa',
author: {
id: '1'
},
receiver: {
id: '2'
}
},
{
id: '101',
text: 'bbb',
author: {
id: '2'
},
receiver: {
id: '1'
}
},
{
id: '102',
text: 'ccc',
author: {
id: '3'
},
receiver: {
id: '1'
}
},
];
function groupByPair(arr) {
return [
...arr.reduce((a, b) => {
let {
author,
receiver
} = b;
let s = [author.id, receiver.id].sort().join('-');
a.set(s, a.has(s) ? a.get(s).concat(b) : [b]);
return a;
}, new Map)
].map(e => ({
chatName: 'Name of user ID ' + e[0].substring(e[0].indexOf('-') + 1),
messages: e[1]
}));
}
console.log(groupByPair(messages));

Using either Lodash or Underscore.js:
var grouped = _.groupBy(messages, m => _.sortBy([m.author.id, m.receiver.id]));
var formatted = _.map(grouped, (v, name) => ({ chatname: name, messages: v }));
You could combine those into a single line, but that seems overly dense to my eyes.
I took the liberty of defining a more complex test dataset, to make sure the edge cases were better covered:
var messages = [
{ id: '100', text: 'aaa', author: { id: '1' }, receiver: { id: '2' } },
{ id: '101', text: 'bbb', author: { id: '2' }, receiver: { id: '1' } },
{ id: '102', text: 'ccc', author: { id: '3' }, receiver: { id: '1' } },
{ id: '103', text: 'zzz', author: { id: '2' }, receiver: { id: '1' } },
{ id: '104', text: 'yyy', author: { id: '3' }, receiver: { id: '4' } },
{ id: '105', text: 'xxx', author: { id: '3' }, receiver: { id: '1' } }
]
With this data, the code above yields a formatted of:
[
{ chatname: '1,2',
messages: [
{ id: '100', text: 'aaa', author: { id: '1' }, receiver: { id: '2' } },
{ id: '101', text: 'bbb', author: { id: '2' }, receiver: { id: '1' } },
{ id: '103', text: 'zzz', author: { id: '2' }, receiver: { id: '1' } }
]
},
{ chatname: '1,3', messages: [
{ id: '102', text: 'ccc', author: { id: '3' }, receiver: { id: '1' } },
{ id: '105', text: 'xxx', author: { id: '3' }, receiver: { id: '1' } }
]
},
{ chatname: '3,4', messages: [
{ id: '104', text: 'yyy', author: { id: '3' }, receiver: { id: '4' } }
]
}
]
The major difference with your desired output relates to the chatname values. I did not see how you were naming those, so I stuck with Lodash/Underscore's native groupby keys.

You can do this with _.groupby. I filter the array first so that you're only left with messages that involve user 1.
const messages = [
{ id: '100', text: 'aaa', author: { id: '1' }, receiver: { id: '2' } },
{ id: '101', text: 'bbb', author: { id: '2' }, receiver: { id: '1' } },
{ id: '102', text: 'ccc', author: { id: '3' }, receiver: { id: '1' } },
];
const filtered = messages.filter((msg) => {
return msg.author.id === '1' || msg.receiver.id === '1'
});
const groups = _.groupBy(filtered, (msg) => {
return msg.author.id === '1' ? msg.receiver.id : msg.author.id
});
console.log(groups);
<script src="https://unpkg.com/lodash#4.17.4"></script>

Related

Is there a way to concatenate parent's value in array of deep nested objects

I have an array of nested objects. I need to update the id property of each node by concatenating all its parent names.
id should be value of the name property of current node joined with name property
od it's parents separated by '/'
treeData = [{
name: 'Infiniti',
id: '',
children: [{
name: 'G50',
id: '',
children: [{
name: 'Pure AWD',
id: ''
},
{
name: 'Luxe',
id: ''
},
],
},
{
name: 'QX50',
id: '',
children: [{
name: 'Pure AWD',
id: ''
},
{
name: 'Luxe',
id: ''
},
],
},
],
},
{
name: 'BMW',
id: '',
children: [{
name: '2 Series',
id: '',
children: [{
name: 'Coupé',
id: ''
},
{
name: 'Gran Coupé',
id: ''
},
],
},
{
name: '3 Series',
id: '',
children: [{
name: 'Sedan',
id: ''
},
{
name: 'PHEV',
id: ''
},
],
},
],
},
];
Expected Outcome
[{
name: 'Infiniti',
id: 'Infiniti',
children: [{
name: 'G50',
id: 'Infiniti/G50',
children: [{
name: 'Pure AWD',
id: 'Infiniti/G50/Pure AWD'
},
{
name: 'Luxe',
id: 'Infiniti/G50/Luxe'
},
],
},
{
name: 'QX50',
id: 'Infiniti/QX50',
children: [{
name: 'Pure AWD',
id: 'Infiniti/QX50/Pure AWD'
},
{
name: 'Luxe',
id: 'Infiniti/QX50/Luxe'
},
],
},
],
},
{
name: 'BMW',
id: 'BMW',
children: [{
name: '2 Series',
id: 'BMW/2 Series',
children: [{
name: 'Coupé',
id: 'BMW/2 Series/Coupé'
},
{
name: 'Gran Coupé',
id: 'BMW/2 Series/Gran Coupé'
},
],
},
{
name: '3 Series',
id: 'BMW/3 Series',
children: [{
name: 'Sedan',
id: 'BMW/3 Series/Sedan'
},
{
name: 'PHEV',
id: 'BMW/3 Series/PHEV'
},
],
},
],
},
];
I tried to use Array.prototype.reduce(), but I am unable to get the previous value to concatinate.
function updateTreeData(array) {
return array.reduce((returnValue, currentValue) => {
if (currentValue.children != null) {
returnValue.push(Object.assign({}, currentValue, {
children: this.updateTreeData(currentValue.children)
}))
}
return returnValue
}, []);
}
console.log(updateTreeData(treeData))
You can try the recursion approach
const treeData=[{name:"Infiniti",id:"",children:[{name:"G50",id:"",children:[{name:"Pure AWD",id:""},{name:"Luxe",id:""}]},{name:"QX50",id:"",children:[{name:"Pure AWD",id:""},{name:"Luxe",id:""}]}]},{name:"BMW",id:"",children:[{name:"2 Series",id:"",children:[{name:"Coupé",id:""},{name:"Gran Coupé",id:""}]},{name:"3 Series",id:"",children:[{name:"Sedan",id:""},{name:"PHEV",id:""}]}]}];
const populateId = (list, currentId) => {
for(const item of list) {
if(!currentId) {
item.id = item.name
} else {
item.id = currentId + '/' + item.name
}
if(item.children) {
populateId(item.children, item.id)
}
}
}
populateId(treeData)
console.log(treeData)

Parsing JSON and grouping multiple array values

I have a JSON response that looks like the following
{
data: [
{
attributes:[
{
type: 'size',
value: '10'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [
{
store_id: '1000',
stock: '10'
},
{
store_id: '2000',
stock: '3'
},
{
store_id: '3000',
stock: '5'
}
]
},
{
attributes:[
{
type: 'size',
value: '9'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [
{
store_id: '1000',
stock: '10'
},
{
store_id: '2000',
stock: '3'
},
{
store_id: '4000',
stock: '5'
}
]
},
{
attributes:[
{
type: 'size',
value: '7'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [
{
store_id: '1000',
stock: '19'
},
{
store_id: '2001',
stock: '8'
},
{
store_id: '4000',
stock: '2'
}
]
}
]
}
Using JS I'm trying to parse the values and get something that sorts each value via the store ID
e.g
using the data above this is what I would like to achieve
array['2000'] = ['10 - (3)', '9 - (3)', '7 - (8)']
2000 being the store_id
array values being size - (stock)
The simple way is to use Array#reduce to aggregate your data.
Besides, in each inventory you should integrate all items to define the value with correct structure like ${attributes[0].value} - (${stock})
const yourJson = {data:[{attributes:[{type:'size',value:'10'},{type:'colour',value:'red'}],inventory:[{store_id:'1000',stock:'10'},{store_id:'2000',stock:'3'},{store_id:'3000',stock:'5'}]},{attributes:[{type:'size',value:'9'},{type:'colour',value:'red'}],inventory:[{store_id:'1000',stock:'10'},{store_id:'2000',stock:'3'},{store_id:'4000',stock:'5'}]},{attributes:[{type:'size',value:'7'},{type:'colour',value:'red'}],inventory:[{store_id:'1000',stock:'19'},{store_id:'2001',stock:'8'},{store_id:'4000',stock:'2'}]}]};
const result = yourJson.data.reduce((acc, {attributes, inventory}) =>
{
for(const {store_id, stock} of inventory){
acc[store_id] = acc[store_id] ||[];
acc[store_id].push(`${attributes[0].value} - (${stock})`);
}
return acc;
}, {});
console.log(result);
You want to use reduce() for that to select the item on the inventory.
Here's an example:
let response = {
data: [
{
attributes:[
{
type: 'size',
value: '10'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [
{
store_id: '1000',
stock: '10'
},
{
store_id: '2000',
stock: '3'
},
{
store_id: '3000',
stock: '5'
}
]
},
{
attributes:[
{
type: 'size',
value: '9'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [
{
store_id: '1000',
stock: '10'
},
{
store_id: '2000',
stock: '3'
},
{
store_id: '4000',
stock: '5'
}
]
},
{
attributes:[
{
type: 'size',
value: '7'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [
{
store_id: '1000',
stock: '19'
},
{
store_id: '2001',
stock: '8'
},
{
store_id: '4000',
stock: '2'
}
]
}
]
}
let selected_inventory = response.data.reduce((a, b) => a = b.inventory.find(inv => inv.store_id === '2000') || a, {})
console.log(selected_inventory) // Expected Result: { "store_id": "2000", "stock": "3" }
The other answer doesn't seem to give it exactly in the format you want.
function toObj(arr) {
const obj = {};
for (const { attributes, inventory } of arr) {
// Watch out, this assumes there will always be a "size" attribute
const size = attributes.find(a => a.type === 'size').value;
for (const inv of inventory) {
// Assign array if it doesn't exist
// This will be, for ex, obj['2000']
obj[inv.store_id] = obj[inv.store_id] || [];
// Constructs and pushes the final strings
obj[inv.store_id].push(size + ' - (' + inv.stock + ')');
}
}
return obj;
}
const INPUT = {
data: [{
attributes: [{
type: 'size',
value: '10'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [{
store_id: '1000',
stock: '10'
},
{
store_id: '2000',
stock: '3'
},
{
store_id: '3000',
stock: '5'
}
]
},
{
attributes: [{
type: 'size',
value: '9'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [{
store_id: '1000',
stock: '10'
},
{
store_id: '2000',
stock: '3'
},
{
store_id: '4000',
stock: '5'
}
]
},
{
attributes: [{
type: 'size',
value: '7'
},
{
type: 'colour',
value: 'red'
}
],
inventory: [{
store_id: '1000',
stock: '19'
},
{
store_id: '2001',
stock: '8'
},
{
store_id: '4000',
stock: '2'
}
]
}
]
}
console.log(toObj(INPUT.data));

How to replace the object with the new object using javascript or lodash?

i have the datastructure like below,
const data = {
getSomething: {
id: '1',
items: [
{
id: '1',
orders: [
{
id: '1',
name: 'one1',
}
],
subItems: [
{ id: '1', name: 'subitem1' },
{ id: '2', name: 'subitem2' },
],
},
{
id: '2',
orders: [
{
id: '2',
name: 'two1',
}
],
subItems: [
{ id: '2', name: 'subitem1' },
{ id: '3', name: 'subitem2' },
],
}
],
}
}
now i have another data like below,
const item1 = {
id: '2',
orders: [
{
id: '3',
name: 'one1',
}
],
}
i have to replace the item with id 1 in data with the item1 above. how can i do it using lodash. or any other way that i can do it easily.
could someone help me with this. thanks.
const data = {
getSomething: {
id: '1',
items: [{
id: '1',
orders: [{
id: '1',
name: 'one1',
}],
subItems: [{
id: '1',
name: 'subitem1'
},
{
id: '2',
name: 'subitem2'
},
],
},
{
id: '2',
orders: [{
id: '2',
name: 'two1',
}],
subItems: [{
id: '2',
name: 'subitem1'
},
{
id: '3',
name: 'subitem2'
},
],
}
],
}
}
const item1 = {
id: '2',
orders: [{
id: '3',
name: 'one1',
}],
}
Object.assign(data.getSomething.items.find(item => item.id === item1.id),item1);
console.log(data);
Are you looking something like this ?
const data = {
getSomething: {
id: '1',
items: [
{
id: '1',
orders: [
{
id: '1',
name: 'one1',
}
],
subItems: [
{ id: '1', name: 'subitem1' },
{ id: '2', name: 'subitem2' },
],
},
{
id: '2',
orders: [
{
id: '2',
name: 'two1',
}
],
subItems: [
{ id: '2', name: 'subitem1' },
{ id: '3', name: 'subitem2' },
],
}
],
}
}
const item1 = {
id: '2',
orders: [
{
id: '3',
name: 'one1.......',
}
],
subItems: [
{ id: '2', name: 'subitem1....' },
{ id: '3', name: 'subitem2....' },
],
}
let result = data.getSomething.items.map(item=> item.id===item1.id ? ({...item1}): item);
console.log('-----Original----', data.getSomething.items);
console.log('-----Updated-----', result);
This will overwrite item with id 1 in data with the item1:
const data = {
getSomething: {
id: '1',
items: [
{
id: '1',
orders: [
{
id: '1',
name: 'one1',
}
],
subItems: [
{ id: '1', name: 'subitem1' },
{ id: '2', name: 'subitem2' },
],
},
{
id: '2',
orders: [
{
id: '2',
name: 'two1',
}
],
subItems: [
{ id: '2', name: 'subitem1' },
{ id: '3', name: 'subitem2' },
],
}
],
}
}
const item1 = {
id: '2',
orders: [
{
id: '3',
name: 'one1',
}
],
}
const oldItem = data.getSomething.items.find(item => item.id == 1);
Object.assign(oldItem, item1);
console.log(data);

How to change the value of BehaviorSubject using map/filter/find/findIndex in angular

TS
arrData = new BehaviorSubject<any>([]);
ngOnInit() {
const dataArr1 = [
{
id: '1',
name: 'Room1',
spinning: true
},
{
id: '2',
name: 'Room2',
spinning: true
},
{
id: '3',
name: 'Room3',
spinning: true
},
{
id: '4',
name: 'Room4',
spinning: true
}
];
this.arrData.next(dataArr1);
const url = { id: '2', name: 'Room2', link: '/api/conditions'}
const arr = new Array();
arr.push(url);
this.arrData.value.map((item: any) => {
return {
id: item.id,
name: item.name,
spinning: arr.findIndex(e => {
return e.id === item.id
}) === -1
}
});
console.log(this.arrData.value);
}
here's the sample output:
https://stackblitz.com/edit/angular-msovvq?file=src/app/app.component.ts
What I'm trying to do here is to change the value of spinning to false.
in the example there's data object { id: 2, name: 'Room2' } then I push it to make it an array.
where I used the findIndex where dataArr1 id === arr id, but the problem here it doesn't change the spinning value to false of dataArr1.
it still the same.
[
{
id: '1',
name: 'Room1',
spinning: true
},
{
id: '2',
name: 'Room2',
spinning: true
},
{
id: '3',
name: 'Room3',
spinning: true
},
{
id: '4',
name: 'Room4',
spinning: true
}
];
it should be like this.
[
{
id: '1',
name: 'Room1',
spinning: true
},
{
id: '2',
name: 'Room2',
spinning: false
},
{
id: '3',
name: 'Room3',
spinning: true
},
{
id: '4',
name: 'Room4',
spinning: true
}
];
the Room2 spinning will change to false.
I suggest you using map along with filter like this. Map and filter return a new array, not change value itself, so rebind the result to the variable.
dataArr1 = dataArr1.map(item => {
return {
id: item.id,
name: item.name,
spinning: dataArr2.findIndex(e => {
return e.id === item.id
}) === -1
}
})
This is the result, is this what you expected?
[
{
"id": "1",
"name": "Room1",
"spinning": false
},
{
"id": "2",
"name": "Room2",
"spinning": false
},
{
"id": "3",
"name": "Room3",
"spinning": false
},
{
"id": "4",
"name": "Room4",
"spinning": true
}
]
Using lodash
let tmp = [
{
id: '1',
name: 'Room1',
spinning: true
},
{
id: '2',
name: 'Room2',
spinning: true
},
{
id: '3',
name: 'Room3',
spinning: true
},
{
id: '4',
name: 'Room4',
spinning: true
}
];
Since we are only using id to find:
let url = { id: '2', name: 'Room2', link: '/api/conditions'}
let _tmp_row= _.find(tmp,{id:url['id']})
_tmp_row.spinning = false
Once you find the element as per your pattern you can change the object property as per you like. Since this object is not cloned it will directly update the changes to the main array of objects.

Javascript array from flat to tree

all,
I have a technical task from one company. As I am starting only the task is quiet a difficult for me.
Tha flat array is:
[
{ id: '1', name: 'name 1', parentId: null },
{ id: '2', name: 'name 2', parentId: null },
{ id: '2_1', name: 'name 2_1', parentId: '2' },
{ id: '2_2', name: 'name 2_2', parentId: '2' },
{ id: '3', name: 'name 3', parentId: null },
{ id: '4', name: 'name 4', parentId: null },
{ id: '5', name: 'name 5', parentId: null },
{ id: '6', name: 'name 6', parentId: null },
{ id: '7', name: 'name 7', parentId: null },
{ id: '1_1', name: 'name 1_1', parentId: '1' },
{ id: '1_2', name: 'name 1_2', parentId: '1' },
{ id: '1_3', name: 'name 1_3', parentId: '1' },
{ id: '1_4', name: 'name 1_4', parentId: '1' },
{ id: '1_5', name: 'name 1_5', parentId: '1' },
{ id: '2_1_1', name: 'name 2_1_1', parentId: '2_1' },
{ id: '2_1_2', name: 'name 2_1_2', parentId: '2_1' },
{ id: '2_1_3', name: 'name 2_1_3', parentId: '2_1' },
{ id: '2_1_4', name: 'name 2_1_4', parentId: '2_1' },
{ id: '2_1_5', name: 'name 2_1_5', parentId: '2_1' },
].
I need to make it as a tree and include it to input list and to have opportunity to open and close children lists. Please help
While creating my solution, I assumed you wanted a accordion-like structure from your data which can be opened or closed based on whether they have children or not. Here is a code snippet for handling link addition.
arr.forEach(a => {
const div = document.createElement("div")
if (a.parentId === null) {
div.classList.add("parent")
div.setAttribute("id", `id${a.id}`)
div.innerText = a.name
tree.appendChild(div)
}
else {
const parent = document.getElementById(`id${a.parentId}`)
if (!parent.classList.contains("parent")) {
parent.classList.add("parent")
}
console.log(`${a.id} `, parent)
let childContainer = null
if (parent.childElementCount === 0) {
childContainer = document.createElement("div")
childContainer.classList.add("childContainer")
parent.appendChild(childContainer)
childContainer.classList.add("hidden")
}
else {
childContainer = document.querySelector(`#id${a.parentId} .childContainer`)
}
div.classList.add("child")
div.setAttribute("id", `id${a.id}`)
div.innerText = a.name
childContainer.appendChild(div)
}
})
Full solution: Codepen link

Categories

Resources