Es6 - Match two different object keys with different name - javascript

I am working on two API's that I don't have permission to change their field names (Sample below)
const api1 = {
studentId: 'abc',
studentName: 'John Doe',
studentAge: 19,
course: 'Engineering',
}
const api2 = {
id,
personalInfo: {
name,
age
},
course:
}
Basically I will need to transfer the data from api2 to api 1 so what I did is:
const payload = {}
payload["studentId"] = api2.id,
payload["studentName"] = api2.personalInfo.name,
payload["studentAge"] = api2.personalInfo.age,
payload["course"] = api2.course,
Is there a way how I can do this dynamically ?

You could take a wrapper object which wraps all wanted properties of the given API to a new API.
This approach takes an object which maps the given properties to new format/names of the wanted structure by getting the entries of the object and iterating these until a non object is found and then it assigns the value to a new object. If an object is found it take this object and gets the values from the nested object.
If nested target properties are supplied, like
{ studentName: 'personalInfo.name' }
(for example for creating API2 from API1) you need to split the value and create an object with a nested structure, like
const
path = value.split('.'),
last = path.pop();
path.reduce((o, k) => o[k] ??= {}, target)[last] = source[key];
in the else part.
const
assign = (source, wrapper, target = {}) => {
Object.entries(wrapper).forEach(([key, value]) => {
if (value && typeof value === 'object') assign(source[key], value, target);
else target[value] = source[key];
});
return target;
},
api1 = { studentId: 'abc', studentName: 'John Doe', studentAge: 19, course: 'Engineering' },
api2 = { id: 'someId', personalInfo: { name: 'someName', age: 'someAge' }, course: 'someCourse' },
wrapper = { id: 'studentId', personalInfo: { name: 'studentName', age: 'studentAge' }, course: 'course' },
payload = assign(api2, wrapper);
console.log(payload);

Related

Recursively update nested object values for specific keys based on another object

*** UPDATED object structure ***
I'd like to update recursively the property values of mainObject from the properties that exist in updatingObject.
let mainObject = {
age: 24,
isMarried: false,
pets: {
dog: {
name: "Juniper",
age: 3
},
cat: {
name: "Spasia",
age: 7
}
},
hobbies: {
mountainRelated: ["hiking", "snowboarding"]
}
}
let updatingObject = {
pets: {
cat: {
age: 8
}
}
}
I've added a Codepen link to the problem below: what I still have to do is to find the correct properties to be updated (for example the "age" property is common for more objects).
TL;DR: cat age should be 8 in the mainObject
https://codepen.io/Dragosb/pen/bGwypKz?editors=0012
You can traverse in sync
let mainObject = {
age: 24,
isMarried: false,
pets: {
dog: { name: "Juniper", age: 3 },
cat: { name: "Spasia", age: 7 }
},
hobbies: {
mountainRelated: ["hiking", "snowboarding"]
}
}
let updatingObject = {
pets: {
cat: {
age: 8,
name:'gabriele'
}
}
}
function updateObject(target, update){
// for each key/value pair in update object
for (const [key,value] of Object.entries(update)){
// if target has the relevant key and
// the type in target and update is the same
if (target.hasOwnProperty(key) && typeof(value) === typeof(target[key])){
// update value if string,number or boolean
if (['string','number','boolean'].includes(typeof value) || Array.isArray(value)){
target[key] = value;
} else {
// if type is object then go one level deeper
if (typeof value === 'object'){
updateObject(target[key], value)
}
}
}
}
}
updateObject(mainObject,updatingObject)
console.log(mainObject);
We can write a function that does this without mutating the original object, in a fairly simple manner. This merge function breaks the original and update objects into key-value pairs, then keeps all those whose keys are only in one of them, and, for those in both, if both values are objects recursively calling merge on them, otherwise choosing the update value. The resulting key-value pairs are then put back into an object.
Here is an implementation:
const merge = (a, b) => Object .fromEntries ([
... Object .entries (a) .filter (([k]) => !(k in b)),
... Object .entries (b) .filter (([k]) => !(k in a)),
... Object. entries (b) .filter (([k]) => (k in a)) .map (([k, v]) =>
[k, Object (v) === v && Object (a [k]) === a [k] ? merge (a [k], v) : v]
),
])
const mainObject = {age: 24, isMarried: false, pets: {dog: { name: "Juniper", age: 3 }, cat: { name: "Spasia", age: 7 }}, hobbies: {mountainRelated: ["hiking", "snowboarding"]}}
const updatingObject = {pets: {cat: {age: 8, name:'gabriele'}}}
console .log (merge (mainObject, updatingObject))
.as-console-wrapper {max-height: 100% !important; top: 0}
Note that while the resulting object is a new one, we use a form of structural sharing between this and the input objects. For instance, if you push 'hangliding' onto the array hobbies.mountainRelated in the resulting object, we will update mainObject values as well. While we could change this behavior, it helps reduce memory consumption, so I wouldn't do so without good reason.
Note that this does not try to deal with more complicated scenarios, such as cyclic objects. It also does not do anything to work with arrays. Arrays add numerous complexities, and if you need them, I would suggest looking at the equivalent functions in a library such as Ramda's mergeDeepRight (disclaimer: I'm an author) or lodash's merge, or dedicated tools like deepmerge.
The below approach solves the problem by first constructing the access path to the property of an object, then access and modify the mainObject according to the access path constructed from the updatingObject.
let mainObject = {
age: 24,
isMarried: false,
pets: {
dog: {
name: 'Juniper',
age: 3,
},
cat: {
name: 'Spasia',
age: 7,
},
},
hobbies: {
mountainRelated: ['hiking', 'snowboarding'],
},
};
let updatingObject = {
pets: {
cat: {
age: 8,
},
},
hobbies: {
mountainRelated: ['biking'],
none: 'not updated',
},
};
function updateGivenObject(mainObject, updatingObject) {
const mainObjPaths = [];
const updateObjPaths = [];
buildPath(mainObject, mainObjPaths);
buildPath(updatingObject, updateObjPaths);
console.log('mainObjPaths:', mainObjPaths);
console.log('updateObjPaths :', updateObjPaths );
updateObjPaths.forEach(path => {
const newValue = getPropByPath(updatingObject, path);
setPropByPath(mainObject, path, newValue);
});
}
function buildPath(obj, accumulatedPaths, currentPaths = []) {
Object.keys(obj).map(key => {
if (typeof obj[key] !== 'object') {
accumulatedPaths.push([...currentPaths, key].join('.'));
} else {
buildPath(obj[key], accumulatedPaths, [...currentPaths, key]);
}
});
}
function getPropByPath(obj, path) {
const pathArr = path.split('.');
let value = obj;
pathArr.forEach(key => {
value = value[key];
});
return value;
}
function setPropByPath(obj, path, newValue) {
const pathArr = path.split('.');
let value = obj;
pathArr.forEach((key, idx) => {
if (value[key] === undefined) {
return;
}
if (idx === pathArr.length - 1) {
value[key] = newValue;
} else {
value = value[key];
}
});
return value;
}
updateGivenObject(mainObject, updatingObject);
console.log(mainObject);
console.log(mainObject.pets.cat.age);
console.log(mainObject.hobbies.mountainRelated[0]);
console.log(mainObject.hobbies.none);
However, the above algorithm depends heavily on shared mutable state and should be careful about it.

How to merge 2 array of objects by id and date in Javascript?

This 2 arrays have multiple objects that has the the same ID but different dates
const names= [
{id:'1',name:'a',date:'1604616214'},
{id:'1',name:'Angel',date:'1604616215'},
{id:'2',name:'b',date:'2004616214'},
{id:'2',name:'Karen',date:'2004616215'},
{id:'3',name:'a',date:'3004616220'},
{id:'3',name:'Erik',date:'3004616221'}
]
const lastnames= [
{id:'1',lastname:'a',date:'4004616220'},
{id:'1',lastname:'Ferguson',date:'4004616221'},
{id:'2',lastname:'b',date:'5004616220'},
{id:'2',lastname:'Nixon',date:'5004616221'},
{id:'3',lastname:'a',date:'6004616222'},
{id:'3',lastname:'Richard',date:'6004616223'}
]
The data is in moment().unix() to create a number "easy to compare"
I want to create a Third array that merge the 2 arrays and create objects with the same id and the last updated date object.
The output should be something like this
const third = [
{id:'1',name:'Angel',lastname:'Ferguson'},
{id:'2',name:'Karen',lastname:'Nixon'},
{id:'3',name:'Erik',lastname:'Richard'}
]
This is what i got so far, if i updated the arrays it duplicates and i need to have only the last updated object
const third = names.map(t1 => ({...t1, ...lastnames.find(t2 => t2.id === t1.id)}))
I'm going to assume since you have the spread operator and Array.find in your example that you can use ES6, which includes for of and Object.values as you see below.
An object and simple looping is used to reduce the amount of times you're iterating. In your example, for every element in names you're iterating over last names to find one with the same ID. Not only is that not ideal for performance, but it doesn't work because every time you're finding the same element with that ID (the first one with that ID in the array).
const names = [
{ id: "1", name: "a", date: "1604616214" },
{ id: "1", name: "Angel", date: "1604616215" },
{ id: "2", name: "b", date: "2004616214" },
{ id: "2", name: "Karen", date: "2004616215" },
{ id: "3", name: "a", date: "3004616220" },
{ id: "3", name: "Erik", date: "3004616221" },
];
const lastnames = [
{ id: "1", lastname: "a", date: "4004616220" },
{ id: "1", lastname: "Ferguson", date: "4004616221" },
{ id: "2", lastname: "b", date: "5004616220" },
{ id: "2", lastname: "Nixon", date: "5004616221" },
{ id: "3", lastname: "a", date: "6004616222" },
{ id: "3", lastname: "Richard", date: "6004616223" },
];
const profiles = {};
function addToProfiles(arr, profiles) {
for (let obj of arr) {
if (obj.id != null) {
// Inits to an empty object if it's not in the profiles objects
const profile = profiles[obj.id] || {};
profiles[obj.id] = { ...profile, ...obj };
}
}
}
addToProfiles(names, profiles);
addToProfiles(lastnames, profiles);
const third = Object.values(profiles);
The idea is to group the objects by their ids, then merge each group according to the rules, maximizing date for each type of record (name and lastname)
// the input data
const names= [
{id:'1',name:'a',date:'1604616214'},
{id:'1',name:'Angel',date:'1604616215'},
{id:'2',name:'b',date:'2004616214'},
{id:'2',name:'Karen',date:'2004616215'},
{id:'3',name:'a',date:'3004616220'},
{id:'3',name:'Erik',date:'3004616221'}
]
const lastnames= [
{id:'1',lastname:'a',date:'4004616220'},
{id:'1',lastname:'Ferguson',date:'4004616221'},
{id:'2',lastname:'b',date:'5004616220'},
{id:'2',lastname:'Nixon',date:'5004616221'},
{id:'3',lastname:'a',date:'6004616222'},
{id:'3',lastname:'Richard',date:'6004616223'}
]
// make one long array
let allNames = [...names, ...lastnames]
// a simple version of lodash _.groupBy, return an object like this:
// { '1': [ { objects with id==1 }, '2': [ ... and so on ] }
function groupById(array) {
return array.reduce((acc, obj) => {
let id = obj.id
acc[id] = acc[id] || [];
acc[id].push(obj);
return acc;
}, {});
}
// this takes an array of objects and merges according to the OP rule
// pick the maximum date name object and maximum date lastname object
// this sorts and searches twice, which is fine for small groups
function mergeGroup(id, group) {
let sorted = group.slice().sort((a, b) => +a.date < +b.date)
let name = sorted.find(a => a.name).name
let lastname = sorted.find(a => a.lastname).lastname
return {
id,
name,
lastname
}
}
// first group, then merge
let grouped = groupById(allNames)
let ids = Object.keys(grouped)
let results = ids.map(id => {
return mergeGroup(id, grouped[id])
})
console.log(results)
I tried to come up with a solution using filter functions. End result contains the format you wanted. check it out.
const names= [
{id:'1',name:'a',date:'1604616214'},
{id:'1',name:'Angel',date:'1604616215'},
{id:'2',name:'b',date:'2004616214'},
{id:'2',name:'Karen',date:'2004616215'},
{id:'3',name:'a',date:'3004616220'},
{id:'3',name:'Erik',date:'3004616221'}
]
const lastnames= [
{id:'1',lastname:'a',date:'4004616220'},
{id:'1',lastname:'Ferguson',date:'4004616221'},
{id:'2',lastname:'b',date:'5004616220'},
{id:'2',lastname:'Nixon',date:'5004616221'},
{id:'3',lastname:'a',date:'6004616222'},
{id:'3',lastname:'Richard',date:'6004616223'}
]
// filter out last updated objects from both arrays
var lastUpdatednames = names.filter(filterLastUpdate,names);
console.log(lastUpdatednames);
var lastUpdatedsurnames = lastnames.filter(filterLastUpdate,lastnames);
console.log(lastUpdatedsurnames);
// combine the properties of objects from both arrays within filter function.
const third = lastUpdatednames.filter(Combine,lastUpdatedsurnames);
console.log(third);
function filterLastUpdate(arrayElement)
{
var max = this.filter( i => arrayElement.id==i.id ).reduce(
function(prev, current)
{
return (prev.date > current.date) ? prev : current
}
)
return max.date == arrayElement.date ;
}
function Combine(firstArray)
{
var subList= this.filter( i => firstArray.id==i.id );
//console.log(subList);
//console.log(subList[0]);
if (subList)
{
firstArray.lastname = subList[0].lastname;
return true;
}
return false ;
}
Here is last output:
[…]
0: {…}
date: "1604616215"
id: "1"
lastname: "Ferguson"
name: "Angel"
1: {…}
date: "2004616215"
id: "2"
lastname: "Nixon"
name: "Karen"
2: {…}
date: "3004616221"
id: "3"
lastname: "Richard"
name: "Erik"

Form dictionary like structure from the given tabs and data

I'm trying to form the dictionary from tabs and data by iterating
I'm unable to form dictionary like structure and expected Output is mentioned
const tabs = {
first: 'first',
second: 'second',
third: 'third'
}
const data = {
accounts: {
members: [
{
node: {id: '1', name: 'first'}
},
{
node: {id: '2', name: 'second'}
},
{
node: {id: '3', name: 'third'}
},
]
}
}
let expectedOutput = {'first': '1','second':'2','third':'3'}
We have an object tabs. We can get key from this object using Object.keys(object_name). It will simply return us an array of key.
Object.keys(tabs) => [first, second,third]
data.accounts.members is also an array.We need to use array filter method to extract the node object from data.accounts.members array. Each item in array is an object. We can use object dot notation property to get value.
const filterarray = data.accounts.members.filter(it => it.node.name === tabs[item])[0]
To read first value from array just simply use filterarray[0] index.
Reduce need initial value which is an empty object.We are appending data in this object.
We can simply add data in object using object[key] = value
Here key will be tabs key. tabs is also an object we need to read the value of that key and store result in object result[tabs[item]] = filterarray.node.id
Once reduce is over we will get result.
const tabs = {
first: 'first',
second: 'second',
third: 'third'
}
const data = {
accounts: {
members: [{
node: {
id: '1',
name: 'first'
}
},
{
node: {
id: '2',
name: 'second'
}
},
{
node: {
id: '3',
name: 'third'
}
},
]
}
}
const expectedOut =
Object.keys(tabs).reduce(
(result, item) => {
const ch = data.accounts.members.filter(it => it.node.name === tabs[item])[0]
result[tabs[item]] = ch.node.id
return result
}, {})
console.log(expectedOut)

Javascript how to push a new element from an initial array to each array inside an array of arrays at the same index?

I want to add new property to an array from another array using indexes of both.
For example, lets say I have the following array:
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
And the array that I want to take from it and push it into initArray:
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
What I need is a final array that be like:
initArray = [
{ name: 'John', family: 'Doe', gender: 'Male'},
{ name: 'Joanna', family: 'Doe', gender: 'Female'}
];
I tried the following:
ngOnInit() {
Object.keys(this.genderArray).forEach((idx) => {
console.log(this.initArray)
this.initArray[idx].push(this.genderArray)
});
console.log(this.initArray)
}
But it returned an error of:
Error: _this.initArray[idx].push is not a function
Here is a stackblitz.
First, you should not push to initArray: you do not add elements to initArray, but instead add a property to each of its elements.
One possible way to do that:
Object.assign(this.initArray[idx], this.genderArray[idx]);
... or, if for some reason you want to create a completely new array of new 'enriched' objects, instead of modifying objects directly:
this.initArray[idx] = {...this.initArray[idx], ...this.genderArray[idx]};
Second, do not use Object.keys() to go through an array: use either forEach() if you do all the transformations inside the function or map, if you return a new element and want to have a new array in the end.
So your function might look like that:
ngOnInit() {
this.genderArray.forEach((element, index) => {
Object.assign(this.initArray[index], element);
});
console.log(this.initArray)
}
... or ...
ngOnInit() {
this.initArray = this.initArray.map((element, index) => {
{ ...element, ...this.genderArray[index] }
});
console.log(this.initArray)
}
Personally, I always find code 'x = x.map(transformer())` highly suspicious (if one reassign anyway, why not use forEach?), but it's up to implementation style choices.
If there's more than property in the elements of an array to be 'mixed', but you only want to take one, you can either go direct:
Object.assign(this.initArray[idx], { gender: this.genderArray[idx].gender });
... use destructuring in assignment:
const { gender } = this.genderArray[idx];
Object.assign(this.initArray[idx], { gender });
... or just bite the bullet and do assignment straight as we did in the good old syntax-sugar-free days:
this.initArray[idx].gender = this.genderArray[idx].gender;
In this particular case the last one looks like the best approach actually. Its disadvantage is having to repeat the property name, but that might also come handy if you want the props to be named differently in source and target arrays.
You're actually trying to "merge" objects in two arrays.
There are many ways to implement this, but I think the clearest one is with the Array.map and Object.assign functions:
const initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
const genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
const ans = initArray.map((person, index) => {
const genderedPerson = Object.assign({}, person, genderArray[index]);
return genderedPerson;
});
console.log(ans);
Array.map iterates an array and modifies its elements.
Object.assign extends an object, adding it additional properties from another object(s).
You can merge two arrays by reduce method using ... spread operator:
const result = initArray.reduce((a, c, i) => {
a.push({...c, ...genderArray[i]})
return a;
},[])
or one line version:
const result = initArray.reduce((a, c, i) => (a.push({...c, ...genderArray[i]}), a),[])
An example:
let initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
let genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
const result = initArray.reduce((a, c, i) => {
a.push({...c, ...genderArray[i]})
return a;
},[])
console.log(result)
The problem is that you're trying to use array.push() on an object, which doesn't work.
Instead, you could use a for loop to go through the array, and simply add a 'gender' category to that index by assigning a value to it, namely the gender at that index in the other array:
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
for (var i = 0; i < initArray.length; i++) {
initArray[i].gender = genderArray[i].gender;
}
console.log(initArray);
Alternatively, you could use array.forEach():
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
initArray.forEach(addGender);
function addGender(_, i) {
initArray[i].gender = genderArray[i].gender;
}
console.log(initArray);

Given an array of objects, how do you create a new array of objects with different key names while also omitting unwanted data (ES6 way)?

Sorry for the title, It's limited to 150 characters.
Full code example:
https://jsfiddle.net/c81zw30m/
Data:
Let's say I make an API request and I get this JSON object returned:
[
{
id: 123,
person: {
data: {
name: 'John',
language: 'Javascript'
}
},
details: {
age: 25
},
has_experience: true
},
{
id: 456,
person: {
data: {
name: 'Peter',
language: null // here we have null as a value.
}
},
details: {
age: 40
},
has_experience: false
},
{
id: 789,
person: {
data: {
name: 'Paul',
language: 'Python'
}
},
details: {
age: 30
},
has_experience: null // and here we also don't know if the person is available
},
];
Goal:
The end goal here is to iterate over the array and end up with new array of objects with different key names. Say for example I want to replace the key of person with human or the key of available with availability.
Additionally (optionally) we want to skip adding keys which value is equal to null.
Current solution:
let results = [];
for (let i=0; i< json.length; i++) {
results.push({
user_id: json[i].id,
name: json[i].person.data.name,
age: json[i].details.age,
has_experience: json[i].available ? json[i].available : false // here we are assigning a value no matter what using a ternary operator, what if we want no key:value pair here, just skip that pair
});
if (json[i].person.data.language) { results[i].language = json[i].person.data.language }
}
console.log(results);
Problem:
Now the example and solution I provided works, but imagine if the original API request had hundreds of key:value pairs, and many of them might be of null value.
Question:
Using modern javascript, is there any less verbose and more clean looking/elegant way to handle this problem?
Overall I am looking to create a brand new array of objects based on the original one, but with new key names where necessary. Additionally, we want to skip adding some of them if the value of the key is null for example.
Cheers.
EDIT:
Changed the key name from the example originally provided from available to has_experience because it was a bit misleading. I am not looking to filter out the original array of objects based on the value of a given key. If I wanted to do that I'd start with filter and then chain on.
What I want to do is to omit adding a key:value pair in the newly formed array if the value of the key is null for example.
Using lodash (or similar), you could get your mapping definition out of the mapping loop.
I find the following reasonably concise, though it can probably be shortened a little further.
import { get, set } from "lodash";
let json = [ ... ];
let mapping = new Map([
["user_id", "id"],
["name", "person.data.name"],
["age", "details.age"],
["availability", "available"],
["language", "person.data.language"],
["some.nested.property", "person.data.language"]
]);
var results = json.map(element => {
var mappedElement = {};
mapping.forEach((path, field, map) => {
var value = get(element, path);
if (value) {
set(mappedElement, field, value);
}
});
return mappedElement;
});
console.log(results);
Running this on your data yields
[Object, Object, Object]
0: Object
user_id: 123
name: "John"
age: 25
availability: true
language: "Javascript"
some: Object
nested: Object
property: "Javascript"
1: Object
user_id: 456
name: "Peter"
age: 40
2: Object
user_id: 789
name: "Paul"
age: 30
language: "Python"
some: Object
nested: Object
property: "Python"
Working example: https://codesandbox.io/s/mnkp79668
You can try something like this
You can achieve with map()
let json = [{id: 123, person: { data: { name: 'John', language: 'Javascript' } }, details: { age: 25 }, has_experience: true },
{id: 456, person: { data: { name: 'Peter',language: null } }, details: { age: 40 }, has_experience: false},
{id: 789, person: { data: { name: 'Paul', language: 'Python' } }, details: { age: 30 }, has_experience: null },];
let results = [];
results = json.map(current => {
let temp = {
user_id: current.id,
name: current.person.data.name,
age: current.details.age,
}
if (current.has_experience) {
temp.availablity = current.has_experience
}
if (current.person.data.language)
{ temp.language = current.person.data.language }
return temp;
})
console.log(results);
You have two separate problems to resolve. The first appears to be a requirement for generic flattening of the nested data structures within the input with out specifying every possible key that might exist.
This function will recursively flatten a nested object, along the way omitting any null values. However, this function might overwrite any values where the same key exists at multiple levels, so see below.
function flatten(obj, dest) {
for (let key in obj) {
if (typeof obj[key] === 'object') {
flatten(obj[key], dest);
} else if (obj[key] !== null) {
dest[key] = obj[key];
}
}
return dest;
}
You also want to re-map some of the keys in your data, where the below function can be used both as a pre-processor to convert known duplicate keys into unique keys, and can also be used as a post-processor to convert particular keys back into nested objects. NB: requires "lodash".
function remap(obj, keys) {
for (let [in_key, out_key] of keys) {
let val = _.get(obj, in_key, null);
if (val !== null) {
_.unset(obj, in_key);
_.set(obj, out_key, val);
}
}
return obj;
}
The functions can be chained together like this:
let in_map = new Map([
['user.id', 'user_id']
]);
let out_map = new Map([
['available', 'test.availability']
]);
let out = data.map(obj => remap(obj, in_map))
.map(obj => flatten(obj, {}))
.map(obj => remap(obj, out_map));
I think what you want is to first filter the list, then map over the filtered results to create the new structure. This may not be especially performant however if the list is quite large.
const list = [
{
id: 123,
person: {
data: {
name: 'John',
language: 'Javascript'
}
},
details: {
age: 25
},
available: true
},
{
id: 456,
person: {
data: {
name: 'Peter',
language: null // here we have null as a value.
}
},
details: {
age: 40
},
available: false
},
{
id: 789,
person: {
data: {
name: 'Paul',
language: 'Python'
}
},
details: {
age: 30
},
available: null // and here we also don't know if the person is available
},
];
const newList = list.filter(listItem => listItem.available).map(filteredItem => {
return {
user_id: filteredItem.id,
name: filteredItem.person.data.name,
age: filteredItem.details.age,
availability: !!filteredItem.available
}
})
document.getElementById('list').innerText = JSON.stringify(list, null, 2);
document.getElementById('newList').innerText = JSON.stringify(newList, null, 2);
.container {
display: flex;
}
.container pre {
flex: 0 0 50%;
}
<div class="container">
<pre id="list"></pre>
<pre id="newList"></pre>
</div>

Categories