Concat array from Object from Array - javascript

I'm currently trying to retrieve a list of metadata stored as an array, inside an object, inside an array. Here's a better explanatory example:
[
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
]
Now, my objective is to retrieve a list of metadata (by their name) without redundancy. I think that .map() is the key to success but I can't find how to do it in a short way, actually my code is composed 2 for and 3 if, and I feel dirty to do that.
The expected input is: ['Author', 'Creator', 'Created', 'Date']
I'm developping in Typescript, if that can help for some function.

You can use reduce() and then map() to return array of names.
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
var result = [...new Set(data.reduce(function(r, o) {
if (o.metadata) r = r.concat(o.metadata.map(e => e.name))
return r
}, []))];
console.log(result)

You could use Set for unique names.
var data = [{ name: 'test', metadata: [{ name: 'Author', value: 'foo' }, { name: 'Creator', value: 'foo' }] }, { name: 'otherTest', metadata: [{ name: 'Created', value: 'foo' }, { name: 'Date', value: 'foo' }] }, { name: 'finalTest' }],
names = new Set;
data.forEach(a => (a.metadata || []).forEach(m => names.add(m.name)));
console.log([...names]);
.as-console-wrapper { max-height: 100% !important; top: 0; }

var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
data
.filter(function(obj){return obj.metadata != undefined})
.map(function(obj){return obj.metadata})
.reduce(function(a,b){return a.concat(b)},[])
.map(function(obj){return obj.name})

A hand to hand Array.prototype.reduce() and Array.prototype.map() should do it as follows;
var arr = [
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
];
result = arr.reduce((p,c) => c.metadata ? p.concat(c.metadata.map(e => e.name))
: p, []);
console.log(result);

Related

How to update environment variable key name dynamically using NodeJS

I'm reading the environment variables (Key & Value) dynamically and forming below array:
commands: [
{
name: 'PRODUCT_NAME',
value: 'iPhone'
},
{
name: 'PRODUCT_PRICE',
value: '1232'
},
{
name: 'PRODUCT_TYPE',
value: 'Electronics'
},
{
name: 'PRODUCT_ID',
value: 'SKU29389438'
},
{
name: 'LOG_ENABLED',
value: 'TRUE'
},
]
I want to update the key name for these two properties dynamically PRODUCT_TYPE -> myapp.property.type.event and PRODUCT_ID -> myapp.property.product.enabled
Final output should look like this:
commands: [
{
name: 'PRODUCT_NAME',
value: 'iPhone'
},
{
name: 'PRODUCT_PRICE',
value: '1232'
},
{
name: 'myapp.property.type.event',
value: 'Electronics'
},
{
name: 'myapp.property.product.enabled',
value: 'SKU29389438'
},
{
name: 'LOG_ENABLED',
value: 'TRUE'
},
]
Please find my product.js code below:
const commands = (Object.entries(process.env).map(([key, value]) => ({ name: key, value })))
console.log("commands : ", commands);
I'm new to Nodejs, can someone please help how can I update these two key dynamically and form the final array?
Your help would be greatly appreciated!
1) You can just loop over and change the name as:
const obj = {
commands: [
{
name: "PRODUCT_NAME",
value: "iPhone",
},
{
name: "PRODUCT_PRICE",
value: "1232",
},
{
name: "PRODUCT_TYPE",
value: "Electronics",
},
{
name: "PRODUCT_ID",
value: "SKU29389438",
},
{
name: "LOG_ENABLED",
value: "TRUE",
},
],
};
obj.commands.forEach((o) => {
if (o.name === "PRODUCT_TYPE") o.name = "myapp.property.type.event";
if (o.name === "PRODUCT_ID") o.name = "myapp.property.product.enabled";
});
console.log(obj.commands);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
2) You can also do as :
one-liner
obj.commands.forEach((o) => (o.name = changes[o.name] ?? o.name));
const obj = {
commands: [{
name: "PRODUCT_NAME",
value: "iPhone",
},
{
name: "PRODUCT_PRICE",
value: "1232",
},
{
name: "PRODUCT_TYPE",
value: "Electronics",
},
{
name: "PRODUCT_ID",
value: "SKU29389438",
},
{
name: "LOG_ENABLED",
value: "TRUE",
},
],
};
const changes = {
PRODUCT_TYPE: "myapp.property.type.event",
PRODUCT_ID: "myapp.property.product.enabled",
};
obj.commands.forEach((o) => {
if (changes[o.name]) o.name = changes[o.name];
});
console.log(obj.commands);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to search a value from an json which has 2 level as return matches from both the object?

I have a array of object which has a inside array which need to be filtered and return array based on matches from both. search is (input) event, which executes on every key press.
stackblitz link stackblitz
list = [
{
id: 'abc',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bus' },
{ key: '3', value: 'bike' },
{ key: '4', value: 'truck' },
{ key: '5', value: 'jeep' },
],
},
{
id: 'def',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bicycle' },
{ key: '3', value: 'train' },
{ key: '4', value: 'aeroplane' },
{ key: '5', value: 'jeep' },
],
},
];
handleSearch = (event) => {
if (event.target.value.length > 0) {
const item = this.list[0].data.filter((items) =>
items.value.toLowerCase().includes(event.target.value.toLowerCase())
);
this.list[0].data = item;
} else {
this.list[0].data = this.orgList;
}
};
expect output
input = car
output = [
{
id: 'abc',
data: [
{ key: '1', value: 'car' },
],
},
{
id: 'def',
data: [
{ key: '1', value: 'car' },
],
},
];
input = truck
output =
[
{
id: 'abc',
data: [
{ key: '4', value: 'truck' },
],
},
];
const list = [{id: 'abc',data: [{ key: '1', value: 'car' },{ key: '2', value: 'bus' },{ key: '3', value: 'bike' },{ key: '4', value: 'truck' },{ key: '5', value: 'jeep' },],},{id: 'def',data: [{ key: '1', value: 'car' },{ key: '2', value: 'bicycle' },{ key: '3', value: 'train' },{ key: '4', value: 'aeroplane' },{ key: '5', value: 'jeep' },],},];
function search(arr, searchVal) {
return arr.map((item) => {
const data = item.data.filter(({ value }) => value === searchVal);
return { ...item, data };
})
.filter(({ data }) => data.length);
}
console.log(search(list, 'car'));
console.log(search(list, 'truck'));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Angular demo
I know that I might be going a bit outside of the scope of your requirements here, but I just simply thought that it might be easier to do it like this.
I just thought that it might be somewhat more scalable this way, if you first flatten the structure, because for arguments sake, let's say that you're data structure needs to become more & more complex overtime, IDK, business requirements change. At least if you have some layer of abstraction to manage that, you can then filter on an array of objects quite simply, like I've done below.
Depending on your needs you may not even need to flatten the structure, it's just my opinion & experience states this to be an easier & more maintainable kinda solution to scale. If you're data structure dose evolve with complexity, where there may be nested structures, you could always look at using some clever little recursive function to flatten your structure.
It's worth also noting that I've added some validation to the search function, while it's probably not a requirement, it's not a bad idea to include such logic, where you could update state on your view model. You could include something like a toast notification, stating that the user has provided an invalid search term, you could be making a request to a server to get this data & you could say that there were no results, etc, I think you get the idea?
I hope that's helped & I'm sorry if I've gone a little OTT. 😅
const list = [
{
id: 'abc',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bus' },
{ key: '3', value: 'bike' },
{ key: '4', value: 'truck' },
{ key: '5', value: 'jeep' },
],
},
{
id: 'def',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bicycle' },
{ key: '3', value: 'train' },
{ key: '4', value: 'aeroplane' },
{ key: '5', value: 'jeep' },
],
},
];
const flattenStructure = data => {
return data.reduce((accumulator, item) => {
const items = item.data.reduce((vehicles, vehicle) => {
const modified = { ...vehicle, id: item.id };
return vehicles.concat(modified);
}, []);
return accumulator.concat(items);
}, []);
};
const search = (array, term) => {
const invalidTerm = term == null || typeof term != 'string' || term.replace(/ /g, '') == '';
const invalidArray = array == null || !Array.isArray(array);
if (invalidTerm || invalidArray) {
console.log("Invalid arguments provided.");
return array;
}
return flattenStructure(array).filter(vehicle => {
const match = vehicle.value.toLowerCase() == term.toLowerCase();
const contains = vehicle.value.toLowerCase().indexOf(term.toLowerCase()) > -1;
return match || contains;
});
};
console.log(search(list, 'car'));
console.log(search(list, 'truck'));
Generaly speaking, when dealing with filtering, avoid using same original array to display filtered results in template.
Concerning filtering function, this should do the trick:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
public list: any;
public orgList: any;
public filteredList: any;
ngOnInit() {
this.list = this.orgList = [
{
id: 'abc',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bus' },
{ key: '3', value: 'bike' },
{ key: '4', value: 'truck' },
{ key: '5', value: 'jeep' },
],
},
{
id: 'def',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bicycle' },
{ key: '3', value: 'train' },
{ key: '4', value: 'aeroplane' },
{ key: '5', value: 'jeep' },
],
},
];
}
filterData = (dataItem, term: string) => {
return dataItem.value.toLowerCase().indexOf(term.toLowerCase()) !== -1;
};
handleSearch = (event) => {
if (event.target.value.length === 0) {
this.filteredList = [];
return;
}
const term = event.target.value;
const temp = this.list.filter((fullItem) =>
fullItem.data.filter((dataItem) => this.filterData(dataItem, term))
);
this.filteredList = temp
.map((fullItem) => ({
...fullItem,
data: fullItem.data.filter((dataItem) =>
this.filterData(dataItem, term)
),
}))
.filter((fullItem) => fullItem.data.length > 0);
};
}

Transform Object attribute to array of object

I want to merge Array of ObjectA containing ObjectB attribute by ObjectA attribute.
For example :
let myArray = [
{ name: 'Jeu', series: { name: 'testA', value: '89' } },
{ name: 'Dim', series: { name: 'testB', value: '490' } },
{ name: 'Dim', series: { name: 'testC', value: '978' } }
]
And I would like to transform it to
[
{ name: 'Jeu', series: { name: 'testA', value: '89' } },
{ name: 'Dim', series: [{ name: 'testB', value: '490' },{ name: 'testC', value: '978' } ] }
]
Am I able to do that with a simple reduce/map loop ?
You can first use reduce (with some spread syntax) to build an object that maps unique names and objects in the format you want to have, grouping series by name. Then, you can simply get the values from this object.
const myArray = [
{ name: 'Jeu', series: { name: 'testA', value: '89' } },
{ name: 'Dim', series: { name: 'testB', value: '490' } },
{ name: 'Dim', series: { name: 'testC', value: '978' } }
];
const map = myArray.reduce(
(acc, curr) => ({
...acc,
[curr.name]: {
name: curr.name,
series: acc[curr.name]
? [...acc[curr.name].series, curr.series]
: [curr.series]
}
}),
{}
);
const output = Object.values(map);
console.log(output);

Javascript filtering nested arrays

I'm trying to filter a on a nested array inside an array of objects in an Angular app. Here's a snippet of the component code -
var teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
What I'm trying to achieve is if I search for m5 for example my result should be -
var teams = [
{ name: 'Team1', members: [] },
{ name: 'Team2', members: [{ name: 'm5' }] },
{ name: 'Team3', members: [] }
];
So I've got teams and filteredTeams properties and in my search function I'm doing -
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = this.teams.map(t => {
t.members = t.members.filter(d => d.name.toLowerCase().includes(value));
return t;
})
}
Now this does work to some extent however because I'm replacing the members it's destroying the array on each call (if that makes sense). I understand why this is happening but my question is what would be the best way to achieve this filter?
you were very close, the only thing that you did wrong was mutating the source objects in teams
basically you can use spread operator to generate a new entry and then return a whole new array with new values.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const value = 'm5';
const result = teams.map(t => {
const members = t.members.filter(d => d.name.toLowerCase().includes(value));
return { ...t, members };
})
console.log(result)
Check this. Instead of hard coded m5 pass your value.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const filteredTeams = teams.map(team => ({ name: team.name, members: team.members.filter(member => member.name.includes('m5')) }));
console.log(filteredTeams);
You are mutating the original objects, but you could assing new properties to the result object for mapping instead.
var teams = [{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] }, { name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] }, { name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }],
result = teams.map(o => Object.assign(
{},
o,
{ members: o.members.filter(({ name }) => name === 'm5') }
));
console.log(result);
console.log(teams);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try to seperate your filter function first:
const filterTeamMembers = (teams, filterArr) => {
const useFilter = filterArr.map(x => x.toLowerCase());
return teams.map(team => ({
...team,
members: team.members.filter(member => useFilter.includes(member.name))
}))
};
// =========== And then:
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = filterTeamMembers(this.teams, [value]);
}

How to display specific JSON data from an API

I'm using got for requests.
Code:
const response = await got(`https://airapi.airly.eu/v2/measurements/installation?installationId=204`, {
headers: {
apikey: `API_KEY`
},
json: true
});
console.log(response.body.current.values);
Output:
[ { name: 'PM1', value: 20.72 },
{ name: 'PM25', value: 32.43 },
{ name: 'PM10', value: 61.22 },
{ name: 'PRESSURE', value: 1028.46 },
{ name: 'HUMIDITY', value: 91.59 },
{ name: 'TEMPERATURE', value: 10.87 } ]
Now, I want to display it to the user in this format:
PM1: 20.72 µg/m3
PM25: 32.43 µg/m3
PM10: 61.22 µg/m3
My question is: What's the best way to do that? In the future, I also want to use a library like https://www.chunqiuyiyu.com/ervy/, so it would be nice to have this data somehow separated. I hope that I've made myself clear :)
It looks like Envy uses a specific structure for its data, so you first need to convert your existing data:
const data = arr.map(el => ({ ...el, key: el.name }));
Then you can map over the elements and join them with line breaks afterwards, printing them to the console.
Demo:
const arr = [{"name":"PM1","value":20.72},{"name":"PM25","value":32.43},{"name":"PM10","value":61.22},{"name":"PRESSURE","value":1028.46},{"name":"HUMIDITY","value":91.59},{"name":"TEMPERATURE","value":10.87}];
// create an envy structure from the original data using `map`
const data = arr.map(el => ({ ...el, key: el.name }));
// `map` over that data and return an array of strings
// separated by line breaks
const str = data.map(el => `${el.key}: ${el.value} µg/m3`).join('\n');
console.log(str);
simply you can do like
var data = [ { name: 'PM1', value: 20.72 },
{ name: 'PM25', value: 32.43 },
{ name: 'PM10', value: 61.22 },
{ name: 'PRESSURE', value: 1028.46 },
{ name: 'HUMIDITY', value: 91.59 },
{ name: 'TEMPERATURE', value: 10.87 } ];
$.each(data, function (i) {
console.log(data[i].name+':'+data[i].value);
});
Lots of ways to do this (without resorting to a library). One way:
let data = [
{ name: 'PM1', value: 20.72 },
{ name: 'PM25', value: 32.43 },
{ name: 'PM10', value: 61.22 },
{ name: 'PRESSURE', value: 1028.46 },
{ name: 'HUMIDITY', value: 91.59 },
{ name: 'TEMPERATURE', value: 10.87 }
];
data.forEach(v => console.log(`${v.name}: ${v.value} µg/m3`));

Categories

Resources