I need to create an array of array.
It is worth noting that the database is very large and that if any attribute does not have a corresponding value, it sends an empty string. I've tried with map and reduce but I wasn't successful:
Any help will be appreciated.
Below I show an example of the expected output:
outputExpected = [
["id", 1, 2],
["name", "name1", "name2"],
["price", 6.95, 998.95],
["promoPrice", 5.91, 333.91],
["category", "test1 | test2", "test3 | test4"],
]
Any way to solve this problem performatically?
this is my code:
let arrayObj = [{
"id": 1,
"name": "name1",
"price": 6.95,
"promoPrice": 5.91,
"category": ["test1, test2"]
},
{
"id": 2,
"name": "name2",
"price": 998.95,
"promoPrice": 333.91,
"category": ["test3, test4"]
}
]
const headers = ["id", "name", "price", "promoPrice", "category"]
const result1 = headers.concat(arrayObj.map((obj) => {
return headers.reduce((arr, key) => {
arr.push(obj[key]) return arr;
}, [])
}))
console.log(result1)
Reduce the array to a Map. On each iteration convert the object to an array of [key, value] pairs using Object.entries(). Use Array.forEach() to iterate the entries and add them to the map. Convert the Map's values iterator to an array using Array.from():
const arr = [{"id":1,"name":"name1","price":6.95,"promoPrice":5.91,"category":["test1", "test2"]},{"id":2,"name":"name2","price":998.95,"promoPrice":333.91,"category":["test3", "test4"]}]
const result = Array.from(arr.reduce((acc, o) => {
Object.entries(o)
.forEach(([k, v]) => {
if(!acc.has(k)) acc.set(k, [k])
acc.get(k).push(Array.isArray(v) ? v.join(' | ') : v)
})
return acc
}, new Map()).values())
console.log(result)
You could simply map the value and check if an item is an array, then take the joined values or the value itself.
const
data = [{ id: 1, name: "name1", price: 6.95, promoPrice: 5.91, category: ["test1, test2"] }, { id: 2, name: "name2", price: 998.95, promoPrice: 333.91, category: ["test3, test4"] }],
headers = ["id", "name", "price", "promoPrice", "category"],
result = data
.reduce(
(r, o) => headers.map((k, i) => [
...r[i],
Array.isArray(o[k]) ? o[k].join(' | ') : o[k]
]),
headers.map(k => [k]),
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
construct one init array with all possible keys with the wanted order, then uses Array.reduce and Array.forEach to Array.push value for per key based on its index.
const arrayObj = [
{
"id":1,
"name":"name1",
"price":6.95,
"promoPrice":5.91,
"category":["test1", "test2"]
},
{
"id":2,
"name":"name2",
"price":998.95,
"promoPrice":333.91,
"category":["test3", "test4"]
}
]
function ConvertToArray2D (items) {
let init = [['id'], ['name'], ['price'], ['promoPrice'], ['category']]
if (!items) return init
return arrayObj.reduce((pre, cur) => {
init.forEach((key, index) => {
pre[index].push(Array.isArray(cur[key[0]]) ? cur[key[0]].join('|') : cur[key[0]])
})
return pre
}, init.slice())
}
console.log(ConvertToArray2D(arrayObj))
This can be handled with a standard 'zip' after mapping your objects to arrays of values in line with the headers array. (This also allows for the result to be pivoted back).
//** #see https://stackoverflow.com/a/10284006/13762301
const zip = (...rs) => [...rs[0]].map((_, c) => rs.map((r) => r[c]));
const headers = ['id', 'name', 'price', 'promoPrice', 'category'];
const arrayObj = [{ id: 1, name: 'name1', price: 6.95, promoPrice: 5.91, category: ['test1', 'test2'] },{ id: 2, name: 'name2', price: 998.95, promoPrice: 333.91, category: ['test3', 'test4'] },];
const result = zip(
headers,
...arrayObj.map((o) => headers.map(h => Array.isArray(o[h]) ? o[h].join(' | ') : o[h]))
);
console.log(result);
// which also allows it to be reversed
console.log(zip(...result));
.as-console-wrapper { max-height: 100% !important; top: 0; }
see: Javascript equivalent of Python's zip function for further zip discussion.
Related
i'm trying to duplicate objects based on two properties that have multiple values differentiated by a comma.
For example:
I have an object
const obj = {
id: 1
date: "2021"
tst1: "111, 222"
tst2: "AAA, BBB"
}
And I would like the result to be an array of 2 objects in this case (because there are 2 values in tst1 OR tst2, these 2 properties will always have the same nr of values differentiated by a comma)
[{
id: 1,
date: "2021",
tst1: "111",
tst2: "AAA",
},
{
id: 1,
date: "2021",
tst1: "222",
tst2: "BBB",
}]
What I tried is this:
I created a temporary object
const tempObject = {
id: obj.id,
date: obj.date,
}
And then I would split and map the property that has multiple values, like this:
cont newObj = obj.tst1.split(",").map(function(value) {
let finalObj = {}
return finalObj = {
id: tempObject.id,
date: tempObject.date,
tst1: value,
})
And now, the newObj is an array of objects and each object contains a value of tst1.
The problem is I still have to do the same for the tst2...
And I was wondering if there is a simpler method to do this...
Thank you!
Here is an example that accepts an array of duplicate keys to differentiate. It first maps them to arrays of entries by splitting on ',' and then trimming the entries, then zips them by index to create sub-arrays of each specified property, finally it returns a result of the original object spread against an Object.fromEntries of the zipped properties.
const mapDuplicateProps = (obj, props) => {
const splitProps = props.map((p) =>
obj[p].split(',').map((s) => [p, s.trim()])
);
// [ [[ 'tst1', '111' ], [ 'tst1', '222' ]], [[ 'tst2', 'AAA' ], [ 'tst2', 'BBB' ]] ]
const dupeEntries = splitProps[0].map((_, i) => splitProps.map((p) => p[i]));
// [ [[ 'tst1', '111' ], [ 'tst2', 'AAA' ]], [[ 'tst1', '222' ], [ 'tst2', 'BBB' ]] ]
return dupeEntries.map((d) => ({ ...obj, ...Object.fromEntries(d) }));
};
const obj = {
id: 1,
date: '2021',
tst1: '111, 222',
tst2: 'AAA, BBB',
};
console.log(mapDuplicateProps(obj, ['tst1', 'tst2']));
Not sure if that's what you're searching for, but I tried making a more general use of what you try to do:
const duplicateProperties = obj => {
const properties = Object.entries(obj);
let acc = [{}];
properties.forEach(([key, value]) => {
if (typeof value === 'string' && value.includes(',')) {
const values = value.split(',');
values.forEach((v, i) => {
if (!acc[i]) {
acc[i] = {};
}
acc[i][key] = v.trim();
});
} else {
acc.forEach(o => o[key] = value);
}
});
return acc;
};
const obj = {
id: 1,
date: '2021',
tst1: '111, 222',
tst2: 'AAA, BBB',
};
console.log(duplicateProperties(obj));
You could start by determining the length of the result using Math.max(), String.split() etc.
Then you'd create an Array using Array.from(), returning the correct object for each value of the output index.
const obj = {
id: 1,
date: "2021",
tst1: "111, 222",
tst2: "AAA, BBB",
}
// Determine the length of our output array...
const length = Math.max(...Object.values(obj).map(s => (s + '').split(',').length))
// Map the object using the relevant index...
const result = Array.from({ length }, (_, idx) => {
return Object.fromEntries(Object.entries(obj).map(([key, value]) => {
const a = (value + '').split(/,\s*/);
return [key, a.length > 1 ? a[idx] : value ]
}))
})
console.log(result)
.as-console-wrapper { max-height: 100% !important; }
what is the most efficient way to group by multiple object properties? It groups by category and then by group.
I have detailed my implementation using a single array reduce which I will push into an array (expected output). I am not sure how to conditionally push to sources array which is created within the reduce.
Without using lodash groupBy or third party. I was also thinking of filtering by a key I create e.g.
const key = `${category}-${group}`;
const items = [
{
"name": "Halloumi",
"group": "Cheese",
"category": "Dairy"
},
{
"name": "Mozzarella",
"group": "Cheese",
"category": "Dairy"
}
];
// my current implementation
const groupedItems = items.reduce((map, item) => {
const { category, name, group } = item;
if (map.has(category)) {
map.get(category).push({
name,
group,
});
} else {
map.set(category, [
{
name,
group,
},
]);
}
return map;
}, new Map());
console.log(Object.fromEntries(groupedItems));
// Another attempt
const groupedItems2 = items.reduce((map, item) => {
const { category, group, name } = item;
acc[category] = acc[category] || { category, themes: [] };
const source = {
id,
name
};
const totalSources = [source].length;
const uniqueSources = [...new Set([source])].length;
acc[category].themes.push({
theme,
totalSources,
uniqueSources,
sources: [source],
});
return acc;
return map;
}, {});
console.log(Object.entries(groupedItems2));
// expected output
[
{
"category": "Dairy",
"groups": [
{
"group": "Cheese",
"totalSources": 2,
"sources": [
{
"name": "Halloumi"
},
{
"name": "Mozzarella"
}
]
}
]
}
]
You need a two-level map.
There are many ways to do that. Here is one:
const items = [{"name": "Halloumi","group": "Cheese","category": "Dairy"},{"name": "Mozzarella","group": "Cheese","category": "Dairy"}];
const dict = {};
for (const {name, group, category} of items) {
((dict[category] ??= {})[group] ??= []).push({name});
}
const categories = Object.entries(dict).map(([category, groups]) => ({
category,
groups: Object.entries(groups).map(([group, sources]) => ({
group,
totalSources: sources.length,
sources
}))
}));
console.log(categories);
I was interested in a more generic approach to this problem. It might or might not be useful for you, but I think it offers another way to think about such problems. The idea is that we have a function which takes a configuration like this:
const config = {
prop: 'category',
childName: 'groups',
children: {
prop: 'group',
childName: 'sources',
totalName: 'totalSources',
children: {prop: 'name'}
}
}
It returns a new function which takes an array of flat objects and nests them, giving totals, and changing property names as necessary. Here is an implementation, tested only on this problem (with substantially more data added to test various scenarios):
const regroup = ({prop, newName = prop, childName, totalName, children}) => (xs) =>
childName && children
? Object .entries (groupBy (x => x [prop]) (xs))
.map (([k, vs]) => [k, vs .map (omit ([prop]))])
.map (([k, vs, kids = regroup (children) (vs)]) => ({
[newName]: k,
... (totalName ? {[totalName]: kids .length} : {}),
[childName]: kids
})
)
: prop && newName
? xs .map (({[prop]: n, ...rest}) => ({[newName]: n, ...rest}))
: [ ...xs]
const groupBy = (fn, k) => (xs) => xs .reduce (
(a, x) => ((k = fn (x)), (a [k] = a [k] || []), (a [k] .push (x)), a)
, {}
)
const omit = (names) => (o) => Object .fromEntries (
Object .entries (o) .filter (([k, v]) => ! names.includes (k))
)
const config = {
prop: 'category',
childName: 'groups',
children: {
prop: 'group',
childName: 'sources',
totalName: 'totalSources',
children: {prop: 'name'}
}
}
const groupFoods = regroup (config)
const items = [{name: "Halloumi", group: "Cheese", category: "Dairy"}, {name: "Mozzarella", group: "Cheese", category: "Dairy"}, {name: "Whole", group: "Milk", category: "Dairy"}, {name: "Skim", group: "Milk", category: "Dairy"}, {name: "Potatos", group: "Root", category: "Vegetable"}, {name: "Turnips", group: "Root", category: "Vegetable"}, {name: "Strawberry", group: "Berry", category: "Fruit"}, {name: "Baguette", group: "Yeast", category: "Bread"}]
console .log (JSON.stringify (groupFoods (items), null, 4))
.as-console-wrapper {max-height: 100% !important; top: 0}
Note the two helper functions. omit clones an object removing certain property names. So, for instance, omit ([age, id]) ({id: 101, first: 'Fred', last: 'Flintstone', age: 27}) returns {first: 'Fred', last: 'Flintstone'} groupBy accepts a function which returns a key value for a value, and returns a function which takes an array of values, and returns an object with the keys found, assbociated with an array of values that generate that key. For instance, groupBy (n => n % 10) ([1, 3, 4, 21, 501, 23, 43, 25, 64]) //=> {"1": [1, 21, 501], "3": [3, 23, 43], "4": [4, 64], "5": [25]}. These are genuinely reusable functions that you might keep in your personal utility library.
The main function simply builds our output using the configuration values supplied. If we need to nest, then we recur on the children node of the configuration.
I am new to javascript. I have an id, name and time that I am trying to get from my data and for each name I am trying to loop through the data and call a function from each name. Any help would be much appreciated. Thank you so much!
This is what I have done
const data = [
[
{
"id": "14hyzdrdsquo",
"name": "Ronald",
"time": '12pm',
},
],
[
{
"id": "1f496w43b8yi",
"name": "Jack",
"time": '1am',
},
],
]
const getData = (id, name, time) => {
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
ids.push(id) // should have each id in this array
names.push(name) // should have each name in this array
times.push(time) // should have each time in this array
}
var id = Math.random().toString(16).slice(2)
data.map(j => j.map(i => getData(id, i.name, i.time)))
Using a for...of loop, you can loop through your array and group on the keys of each object. Since you have arrays in your outer array, you can use another for loop to loop over those and get each object. Then you can use a for...in loop to over the keys in your object. For each key, you can check if it exists within grouped, and if it does, concatenate the object's value to the grouped array. If it doesn't exist, you can create a new element, and push the value. Once you have grouped everything, you can use destructuring to pull out the array values from your object into variables:
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const grouped = {};
for(const arr of data) {
for(const obj of arr) {
for(const key in obj) {
grouped[key] = (grouped[key] || []).concat(obj[key]);
}
}
}
const {id, name, time} = grouped;
console.log(id);
console.log(name);
console.log(time);
The above concept can be achieved with .reduce() as well, where the grouped array gets built by using the accumulator argument of the reduce method, and each object is iterated using Object.entries() with .forEach():
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const grouped = data.reduce((acc, arr) => {
arr.forEach(obj => {
Object.entries(obj).forEach(([key, val]) => {
acc[key] = [...(acc[key] || []), val];
});
})
return acc;
}, {});
const {id, name, time} = grouped;
console.log(id);
console.log(name);
console.log(time);
Lastly, if you're happy with doing multiple iterations through your array, you can use .flatMap() to iterate through your array, and for each array, .map() over the objects inside of that. For each object you can extract the key using destructuring assignment. The result of the inner map is then flattened into the outer resulting array due to .flatMap():
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const id = data.flatMap(arr => arr.map(({id}) => id));
const name = data.flatMap(arr => arr.map(({name}) => name));
const time = data.flatMap(arr => arr.map(({time}) => time));
console.log(id);
console.log(name);
console.log(time);
You could destructure the double nested array and take the entries of the object and push the values to the same named properties with 's'.
Ath the end destructure the objct for single arrays.
const
data = [[{ id: "14hyzdrdsquo", name: "Ronald", time: '12pm' }], [{ id: "1f496w43b8yi", name: "Jack", time: '1am' }]],
{ ids, names, times } = data.reduce((r, [o]) => {
Object.entries(o).forEach(([k, v]) => (r[k + 's'] ??= []).push(v));
return r;
}, {});
console.log(ids);
console.log(names);
console.log(times);
The problems is you put
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
inside getData function. That's mean you created new one arrays time when call getData
and for you solution better use .forEach instead of .map.
const data = [
[
{
"id": "14hyzdrdsquo",
"name": "Ronald",
"time": '12pm',
},
],
[
{
"id": "1f496w43b8yi",
"name": "Jack",
"time": '1am',
},
],
]
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
const getData = (id, name, time) => {
ids.push(id) // should have each id in this array
names.push(name) // should have each name in this array
times.push(time) // should have each time in this array
}
var id = Math.random().toString(16).slice(2)
data.forEach(j => j.forEach(i => getData(id, i.name, i.time)))
console.log(ids)
console.log(names)
console.log(times)
You may do that using reduce inside each loop so you can have lower complexity.
const data = [
[
{
id: '14hyzdrdsquo',
name: 'Ronald',
time: '12pm',
},
{
id: '14hyzdrdsquo',
name: 'Ronald',
time: '12pm',
},
],
[
{
id: '1f496w43b8yi',
name: 'Jack',
time: '1am',
},
{
id: '1f496w43b8yi',
name: 'Jack',
time: '1am',
},
],
];
let ids = [];
let names = [];
let times = [];
data.forEach((elem) => {
const obj = elem.reduce(
(acc, curr, i) => {
acc.ids.push(curr.id);
acc.names.push(curr.name);
acc.times.push(curr.time);
return acc;
},
{
ids: [],
names: [],
times: [],
}
);
ids = [...ids, ...obj.ids];
names = [...names, ...obj.names];
times = [...times, ...obj.times];
});
console.log({
ids,
names,
times,
});
This is my data with 5 arrays. What I wish to achieve is to combine id and name and the new array should have 5 different playname values. It can be in either an array or new key like playername1.
[
{
"id": 1,
"name": "Liquid",
"playername": "GH",
},
{
"id": 1,
"name": "Liquid",
"playername": "KuroKy",
},
{
"id": 1,
"name": "Liquid",
"playername": "Miracle",
},
{
"id": 1,
"name": "Liquid",
"playername": "w33",
},
{
"id": 1,
"name": "Liquid",
"playername": "Mind-Control",
}
]
I am using lodash to try and achieve this but I am not able to get the data format I want using the code examples I have searched online.
This is my current code that I have tried that gives an array that is grouped by the ID.
_.forOwn(this.state.teamsData, function(value, key) {
console.log(value);
});
The original data are not grouped by ID.
I am trying to get my data to look like this {"id": 1, "name": liquid, "playername": "GH", "playername2": "KuroKy" ....}
You could group by id and name properity and store the index for the same group.
var data = [{ id: 1, name: "Liquid", playername: "GH" }, { id: 1, name: "Liquid", playername: "KuroKy" }, { id: 1, name: "Liquid", playername: "Miracle" }, { id: 1, name: "Liquid", playername: "w33" }, { id: 1, name: "Liquid", playername: "Mind-Control" }],
result = Object
.values(data.reduce((r, { id, name, playername }) => {
var key = [id, name].join('|');
r[key] = r[key] || { data: { id, name }, index: 0 };
r[key].data['playername' + (r[key].index++ || '')] = playername;
return r;
}, {}))
.map(({ data }) => data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Group by a combination of id and name (${o.id}~~~${o.name}). Map the groups, extract the name and id from the 1st item, take the player names, and use _.mapKeys() to convert the indexes to object keys. Combine the id, name, and playername properties to a single object using spread.
const teamsData = [{"id":1,"name":"Liquid","playername":"GH"},{"id":1,"name":"Liquid","playername":"KuroKy"},{"id":1,"name":"Liquid","playername":"Miracle"},{"id":1,"name":"Liquid","playername":"w33"},{"id":1,"name":"Liquid","playername":"Mind-Control"}]
const result = _(teamsData)
.groupBy(o => `${o.id}~~~${o.name}`) // group by id and name
.map(group => ({ // map the groups
..._.pick(_.head(group), ['id', 'name']), // take id and name from 1st item
..._.mapKeys(_.map(group, 'playername'), // extract the player names
(v, k) => `playername${+k > 0 ? +k + 1 : ''}` // create the keys
)
}))
.value()
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
You can use reduce and Map. Here Map is used to keep track of number of palyername is used for particular id
const arr = [{"id":1,"name":"Liquid","playername":"GH"},{"id":1,"name":"Liquid","playername":"KuroKy"},{"id":1,"name":"Liquid","playername":"Miracle"},{"id":1,"name":"Liquid","playername":"w33"},{"id":1,"name":"Liquid","playername":"Mind-Control"}]
let groupData = (arr) => {
let mapper = new Map()
return Object.values(arr.reduce((op,{id, name, playername }) => {
mapper.set(id, ( mapper.get(id) || 0) + 1 )
let key = mapper.get(id)
op[id] = op[id] || { id, name }
op[id][`playername${key}`] = playername
return op
},{}))
}
console.log(groupData(arr))
Just using using reduce to group the array into an object and Object.values to convert the object into an array.
let list = [
{"id": 1,"name": "Liquid","playername": "GH",},
{"id": 2,"name": "Solid","playername": "KuroKy",},
{"id": 1,"name": "Liquid","playername": "Miracle",},
{"id": 1,"name": "Liquid","playername": "w33",},
{"id": 2,"name": "Solid","playername": "Mind-Control",}
];
let counter = {};
let result = Object.values(list.reduce((c, v) => {
if (!c[v.id]) {
counter[v.id] = 0;
c[v.id] = {...v};
} else c[v.id]["playername" + ++counter[v.id]] = v.playername;
return c;
}, {}));
console.log(result);
The data could have been structured better. With the given structure, following code is one way to solve it.
let data = [
{"id": 1,"name": "Liquid","playername": "GH",},
{"id": 2,"name": "Solid","playername": "KuroKy",},
{"id": 1,"name": "Liquid","playername": "Miracle",},
{"id": 1,"name": "Liquid","playername": "w33",},
{"id": 2,"name": "Solid","playername": "Mind-Control",}
]; // Your data
let new_data = {}; // New structured data
data.map(function(data_object) {
let team = new_data['id'+data_object.id];
if(team==null) {
// Creates a new object in new_data if an object
// for the id does not exists.
new_data['id'+data_object.id] = team = {};
team.players = [];
}
team.id = data_object.id;
team.name = data_object.name;
team.players.push(data_object.playername);
});
console.log(new_data);
With this code, you will have a new_data object of format
{
id1 : {
id : 1,
name : Liquid,
players : ['GH', 'Miracle', 'w33']
},
id2 : {
id : 2,
name : Solid,
players : ['Kuroky', 'Mind-control']
}
}
I have a data object like this :
{
"data1": [
[
"ID",
"name",
"Birthday"
],
[
"10",
"thomas",
"1992-03-17"
],
[
"11",
"Emily",
"2000-03-03"
]
],
"data2": [
[
"Balance",
"ID"
],
[
"$4500",
"10"
],
[
"$1500",
"13"
]
]
}
It contains two arrays data1 and data2.
The first row in each array is the name of the columns and the rest of the rows have the data (think of it like a table).
I want to compare the ID field in both arrays and if the IDs match then the final output will contain a column Balance with the balance corresponding to that ID and if the IDs don't match then the Balance will be $0.
Expected output:
{
"output": [
[
"ID",
"name",
"Birthday",
"Balance"
],
[
"10",
"thomas",
"1992-03-17",
"$4500" //ID 10 matched so the balance added here
],
[
"11",
"Emily",
"2000-03-03",
"0" //0 bcoz the ID 11 is not there in data2 array
]
]
}
I find this challenging to accomplish. Think of it like a LEFT-JOIN in MySQL.
I referred to this solution but it doesn't work in my case as I don't have the keys in my response.
EDIT: I also need to join the other fields as well.
You can use Array.prototype.map(), find, filter, slice, reduce, concat, includes and Object.assign().
This solution:
Handles arbitrary ordering of the items. The order is read from the headers.
Appends a Balance field only if there is one present in data2.
Joins all other fields (requested by OP, see comments below).
Takes default values as an input and uses them if the data is not present in data1 and data2.
function merge({ data1, data2 }, defaults) {
// get the final headers, add/move 'Balance' to the end
const headers = [...data1[0].filter(x => x !== 'Balance')]
.concat(data2[0].includes('Balance') ? ['Balance'] : []);
// map the data from data1 to an array of objects, each key is the header name, also merge the default values.
const d1 = data1.slice(1)
.map(x => x.reduce((acc, y, i) => ({ ...defaults, ...acc, [data1[0][i]]: y }), {}));
// map the data from data2 to an array of objects, each key is the header name
const d2 = data2.slice(1)
.map(x => x.reduce((acc, y, i) => ({ ...acc, [data2[0][i]]: y }), {}));
// combine d1 and d2
const output = d1.map((x, i) => { // iterate over d1
// merge values from d2 into this value
const d = Object.assign(x, d2.find(y => y['ID'] === x['ID']));
// return an array ordered according to the header
return headers.map(h => d[h]);
});
return { output: [headers, ...output] };
}
const test0 = {
data1: [[ "ID","name","Birthday","other"],["10","thomas","1992-03-17","empty"],["11","Emily","2000-03-03","empty"]],
data2: [["other", "ID", "Balance", "city"],["hello", "10", "$4500", "New York"],["world", "10","$8","Brazil"]]
};
const test1 = {
data1: [["ID","name","Birthday"],["10","thomas","1992-03-17"],["11","Emily","2000-03-03"]],
data2: [["other","ID"],["x","10"],["y","11"]]
};
console.log(merge(test0, { Balance: '$0' }));
console.log(merge(test1, { Balance: '$0' }));
const KEY_ID = "ID";
var data = {
"data1": [
[ "ID", "name", "Birthday" ],
[ "10", "thomas", "1992-03-17" ],
[ "11", "Emily", "2000-03-03" ]
],
"data2": [
[ "Balance", "ID" ],
[ "$4500", "10" ],
[ "$1500", "13" ]
]
}
var merged = Object.keys(data).map(function (key) {
var tmp = data[key].slice();
var heads = tmp.shift();
return tmp.map(function (item) {
var row = {};
heads.forEach(function (head, i) {
row[head] = item[i];
});
return row;
});
}).flat().reduce(function (acc, row) {
var found = acc.find(function (item) {
return row[KEY_ID] === item[KEY_ID];
})
if (!found) {
found = row;
acc.push(found);
} else {
Object.keys(row).forEach(function (head) {
found[head] = row[head];
});
}
return acc;
}, []);
console.log(merged);
This solution is scalable: if you add properties, it will scale the new format.
let a = { "data1": [ ... ],"data2": [ ...] }
let r = a.data1.reduce((r,u,i)=>{
if(i !== 0)
{
let entry = a.data2.filter((a)=> a[1]===u[0])
r.push([...u,entry.length ? entry[0][0] : 0])
}
return r
},[[
"ID",
"name",
"Birthday",
"Balance"
]])
You could abstract all table operations into a class-like:
function Table(array) {
const [head, ...values] = array;
const Entry =(entry) => ({
get(key) { return entry[ head.indexOf(key) ]; },
set(key, value) { entry[ head.indexOf(key) ] = value; }
});
return {
index(name) {
const result = {};
for(const value of values)
result[ value[ head.indexOf(name) ] ] = Entry(value);
return result;
},
*[Symbol.iterator]() {
for(const row of values)
yield Entry(row);
},
addRow(key) { head.push(key); }
};
}
Usable as:
const users = Table(obj.data1);
const balances = Table(obj.data2);
const balanceByID = balance.index("ID");
users.addRow("Balance");
for(const user of users)
user.set("Balance", balanceByID[ user.get("ID") ].get("Balance"));