Change the structure of my array [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'd like to change the structure of my result.There are objects "itemGroup" and I'd like to delete them and keep keys "fruit" and "vegetable".
[{"id": 1, "shop": "shop1", "itemGroup": {"fruit": 2, "vegetable": 2},"total":4},
{"id": 2, "shop": "shop2", "itemGroup": {"fruit": 0, "vegetable": 1},"total":1}]
I'd like this result
[
{ "id": 1, "shop": "shop1", "fruit": 2, "vegetable": 2, "total": 4 },
{ "id": 2, "shop": "shop2" "fruit": 0, "vegetable": 1, "total": 1 }
]
my code
var myArray = [
{shop: "shop1",item1: "my apple 1",item2: "my carrot 1"},
{shop: "shop1",item1: "my apple 1",item2: "my carrot 1"},
{shop: "shop2",item1: "my apple 0",item2: "my carrot 0"},
{shop: "shop2",item1: "my apple 0",item2: "my carrot 1"}
];
var MyArrayDefinition = [
{item: "my apple 0",color: "red", group: "fruit",score: 0},
{item: "my carrot 1",color: "orange",group: "vegetable",score: 1},
{item: "my apple 1",color: "red", group: "fruit",score: 1},
{item: "my carrot 0",color: "orange",group: "vegetable",score: 0}
];
var k = Object.keys,
items = MyArrayDefinition.reduce((o, v) => (o[v.item] = v, o), {});
var shops = myArray.reduce((o, v, i, s) => (
s = v[k(v).find(k => k)],
s = o[s] || (o[s] = {
fruit: 0,
vegetable: 0,
}),
k(v).forEach(k => k.includes('item') &&
(s[(i = items[v[k]]).group] += i.score)), o), {});
var result = k(shops).map((k, i) => ({
id: i + 1,
shop: k,
itemGroup: shops[k],
total:Object.values(shops[k]).reduce((a, b) => a + b),
}));

Pretty much like in most of your questions from the last couple of days. :-)
You can map over the data, use Object.assign and delete the itemGroup.
let x = f.map(e => {
e = Object.assign(e, e.itemGroup);
delete e.itemGroup;
return e;
})
console.log(x);
<script>
let f = [{
"id": 1,
"shop": "shop1",
"itemGroup": {
"fruit": 2,
"vegetable": 2
},
"total": 4
},
{
"id": 2,
"shop": "shop2",
"itemGroup": {
"fruit": 0,
"vegetable": 1
},
"total": 1
}
]
</script>

Expounding on your "original question" set the percentages of each item in the store, and answering your "modified question" change the structure of my array, this gives you both by modifying your original code.
let myArray = [{"shop":"shop1","item1":"my apple 1","item2":"my carrot 1"},{"shop":"shop1","item1":"my apple 1","item2":"my carrot 1"},{"shop":"shop2","item1":"my apple 0","item2":"my carrot 0"},{"shop":"shop2","item1":"my apple 0","item2":"my carrot 1"}]
let MyArrayDefinition = [{"item":"my apple 0","color":"red","group":"fruit","score":0},{"item":"my carrot 1","color":"orange","group":"vegetable","score": null},{"item":"my apple 1","color":"red","group":"fruit","score":1},{"item":"my carrot 0","color":"orange","group":"vegetable","score":0}]
let k = Object.keys
let items = MyArrayDefinition.reduce((o, v) => (o[v.item] = v, o), {})
let shops = myArray.reduce(function (o, v, i, s) {
return s = v[k(v).find(function (k) {
return k;
})], s = o[s] || (o[s] = {
fruit: 0,
vegetable: 0
}), k(v).forEach(function (k) {
return k.includes('item') && (s[(i = items[v[k]]).group] += i.score);
}), o;
}, {});
// Helper function that calculates percentage
function percentage (amount, total) {
if (total === 0) { // added check for 0 divisor
return `0%`
}
return `${(amount / total) * 100}%`
}
let result = k(shops).map((k, i) => {
let total = Object.values(shops[k]).reduce((a, b) => a + b) | 0 // added check if number else 0
let fruit = shops[k].fruit | 0 // added check if number else 0
let veg = shops[k].vegetable | 0 // added check if number else 0
return {
id: i + 1,
shop: k,
fruit: fruit,
vegetable: veg,
total: total,
fruitPercentage: percentage(fruit, total),
vegetablePercentage: percentage(veg, total)
}
})
console.log(JSON.stringify(result, null, 2))
/** result from console.log()
*
[
{
"id": 1,
"shop": "shop1",
"fruit": 2,
"vegetable": 2,
"total": 4,
"fruitPercentage": "50%",
"vegetablePercentage": "50%"
},
{
"id": 2,
"shop": "shop2",
"fruit": 2,
"vegetable": 0,
"total": 2,
"fruitPercentage": "100%",
"vegetablePercentage": "0%"
}
]
* */

Using map is the way to transpose the data from one array onto another and run calculations if needed.
// Create a function that takes in your result array
function setPercentage (array) {
// Helper function that calculates percentage
function percentage (amount, total) {
return (amount / total) * 100
}
// Map the results of the input array onto a new array,
// and return the result
return array.map((obj) => {
return {
id: obj.id,
shop: obj.shop,
fruit: percentage(obj.itemGroup.fruit, obj.total),
vegetable: percentage(obj.itemGroup.vegetable, obj.total),
total: obj.total
}
})
}
// pass in the result array from your code...
const shops_by_percentage = setPercentage(result)
console.log(shops_by_percentage)
/** result in the console.log()
*
[
{
'id': 1,
'shop': 'shop1',
'fruit': 50,
'vegetable': 50,
'total': 4
},
{
'id': 2,
'shop': 'shop2',
'fruit': 0,
'vegetable': 100,
'total': 1
}
]
*
* */

Below you can find general solution to your problem.
Using this approach you can create unlimited number of items in your items array as well as unlimited number of groups in your definitions and your code will still work as expected. Lastly your score value acts as weight (when you give some item for example score 2 each occurrence will count as two items).
// Your items
const items = [
{
shop: "shop1",
item1: "my apple 1",
item2: "my carrot 1",
},
{
shop: "shop1",
item1: "my apple 1",
item2: "my carrot 1"
},
{
shop: "shop2",
item1: "my apple 0",
item2: "my carrot 0"
},
{
shop: "shop2",
item1: "my apple 0",
item2: "my carrot 1"
},
];
// Your definitions
const definitions = [
{
item: "my apple 0",
color: "red",
group: "fruit",
score: 0
},
{
item: "my carrot 1",
color: "orange",
group: "vegetable",
score: 1
},
{
item: "my apple 1",
color: "red",
group: "fruit",
score: 1
},
{
item: "my carrot 0",
color: "orange",
group: "vegetable",
score: 0
}
];
function groupShops(items) {
return items.reduce((acc, cur) => {
// Find shop with id of current item in accumulator
const currentShop = acc.find(shop => shop.id === cur.shop);
// Get all shop items
const shopItems = Object.keys(cur)
// Filter shop key as it is shop's ID
.filter(key => key !== 'shop')
// Map keys into values
.map(key => cur[key]);
// If shop already exists in accumulator
if (!!currentShop) {
// Return updated accumulator
return acc
// Remove current shop
.filter(shop => shop !== currentShop)
// And add new copy of current shop with new items to the accumulator
.concat({
id: currentShop.id,
items: currentShop.items.concat(shopItems),
});
}
// If shop doesn't exist in accumulator add it there and return updated accumulator
return acc.concat({
id: cur.shop,
items: shopItems,
});
}, []);
};
function extendItems(shops) {
// Filter items which have score 0 or less
const filterItems = items => items.filter(item => item.score > 0);
// Map though shops
return shops.map(shop => {
// Return updated shop
return {
// Keep shop id
id: shop.id,
// Extend itemIds by the properties stored in the definition and filter them
items: filterItems(shop.items.map(item => definitions.find(definition => definition.item === item))),
}
});
}
function calculateResult(shop, index) {
// Get all available groups
const availableGroups = definitions.reduce((acc, cur) => acc.indexOf(cur.group) > -1 ? acc : acc.concat(cur.group), []);
// Calculate total possible score
const getTotalScore = () => shop.items.reduce((acc, cur) => cur.score + acc, 0);
// Get score of a passed group
const getGroupScore = group => shop.items.reduce((acc, cur) => cur.group === group ? acc + cur.score : acc, 0);
// Loop though each available group and get its score
const resultData = availableGroups.reduce((acc, cur) => {
return {
// Copy data from accumulator
...acc,
// Add new property to the accumulator with a property key {group name} and value {percantage}
[cur]: getGroupScore(cur, shop.items) / getTotalScore(shop.items) * 100,
}
}, {});
// Return result object
return {
// Destruct items of the result object
...resultData,
// Store total items count
total: shop.items.length,
// Store shop id
shop: shop.id,
// Store index
id: index,
}
}
// Groups shops
const groupedShops = groupShops(items);
// Groups shops with extended items
const extendedShops = extendItems(groupedShops);
// You result object
const result = extendedShops.map((shop, index) => calculateResult(shop, ++index));
console.log(result);

Related

Building a table from an array in React

I have an array stating salary of different sectors. I need to calculate and then create a table based on this . I'm really confused how to do it . Here is an example data
const data=[
['Euro','Tech'],
['USD','Tech'],
['GBX', 'Health'],
['Euro', 'Real Estate'],
['GBX', 'Real Estate'].
]
Now I have to display the sector ,salary & total in the Table like this below:
Sector
Euro
GBX
USD
Total
Tech
1
0
1
2
Health
0
1
0
1
Real Estate
1
1
0
2
Total
2
2
1
5
Could you help me with this. I'm using React table to display the data.
Here the idea is to create a two dimensional array filled with zeros, then work through each item in the data array and increment the correct value in the two diminsional array based on a lookup using a map of sector names to indexes and a map of currency names to indexes.
Hopefully it helps, though I feel like there's probably a better way to do it.
const data = [
['Euro','Tech'],
['USD','Tech'],
['GBX', 'Health'],
['Euro', 'Real Estate'],
['GBX', 'Real Estate'],
['GBX', 'Health'],
];
const array_unique = (arr) => Array.from(new Set(arr));
const sectors = array_unique( data.map(([_, sector]) => sector) );
const currencies = array_unique( data.map(([currency]) => currency) ).sort();
const sector_map = Object.fromEntries(
sectors.map((sector, index) => [sector, index])
);
const currency_map = Object.fromEntries(
currencies.map((currency, index) => [currency, index])
);
// zero fill 2 dimensional array
const values_matrix = sectors.map( () => currencies.map(() => 0) );
// increment values in the 2 dimensional array for each item in the data array
// getting the row index and column index from the sector_map and currency_map
for(const [currency, sector] of data) {
values_matrix[ sector_map[sector] ][ currency_map[currency] ]++;
}
// create rows for a table
const rows = [['Sector', ...currencies]];
for(const [index, sector] of sectors.entries()) {
rows.push([sector, ...values_matrix[index]]);
}
console.log(rows.map((row) => row.join(',')));
try this code to format data array
const data = [
['Euro', 'Tech'],
['USD', 'Tech'],
['GBX', 'Health'],
['Euro', 'Real Estate'],
['GBX', 'Real Estate'],
];
let res = {};
let currencies = [];
data.map((item) => {
const [val, key] = item;
res[key] ? res[key].push(val) : (res[key] = [val]);
if (!currencies.includes(val)) currencies.push(val);
});
const rows = Object.keys(res); //Tech, Health, Real Estate
const table = new Array(rows.length);
let index = 0;
for (let row = 0; row < currencies.length; row++) {
key = rows[row];
const values = Object.values(res[key]);
table[index] = { title: key, values: new Array(currencies.length) };
for (let i = 0; i < currencies.length; i++) {
const element = currencies[i];
if (values.includes(element)) {
table[index].values[i] = { title: element, value: 1 };
} else {
table[index].values[i] = { title: element, value: 0 };
}
}
index++;
}
console.log(JSON.stringify(table, null, 2));
result will be like this
[
{
"title": "Tech",
"values": [
{
"title": "Euro",
"value": 1
},
{
"title": "USD",
"value": 1
},
{
"title": "GBX",
"value": 0
}
]
},
{
"title": "Health",
"values": [
{
"title": "Euro",
"value": 0
},
{
"title": "USD",
"value": 0
},
{
"title": "GBX",
"value": 1
}
]
},
{
"title": "Real Estate",
"values": [
{
"title": "Euro",
"value": 1
},
{
"title": "USD",
"value": 0
},
{
"title": "GBX",
"value": 1
}
]
}
]

How can i compare two arrays of different length and keys on the basis of matching key values?

I have tried this, but it is giving the correct result
Array1
Array1: [
{
id: 2,
city: 'washington',
code: 0099,
room: 5,
...
},
{
...
},
...
]
Array 2
Array2: [
{
"id": 2,
"name": "john"
"number": 727625,
"etage": 5,
"status": 0,
...
},
{
...
},
...
]
My Code
let Result = [];
if (Array1 && Array1.length > 0 && Array2 && Array2.length > 0) {
Array1.forEach((arr1, index) => {
Array2.forEach((arr2, index) =>{
if (arr1.id === arr2.id && arr1.room === arr2.etage) {
Result.push(arr1)
}
})
})
}
console.log(Result)
What I want ?
I want items(objects) of Array1 by comparing both arrays, where
both have same id's && room from Array1's object equal to the etage
from Array2's object.
Please guide me, how can I do this in ES6 style in React js?
The main problem with nested loops is the unnecessary iteration of each element of the first array and multiple iterations of the second array.
This approach takes two loops, one for generating all keys from array2 and the other to filter array1.
You could take a Set for compound key of id and etage and filte the array for getting the items with same id and room.
const
getKey = (...values) => values.join('|'),
keys = new Set(array2.map(({ id, etage }) => getKey(id, etage))),
result = array1.filter(({ id, room }) => keys.has(getKey(id, room));
With condition
room > etage
and a Map.
const
ids = array2.reduce(
(m, { id, etage }) => m.set(id, Math.min(etage, m.get(id) || 0)),
new Map
),
result = array1.filter(({ id, room }) => room > ids.get(id));
I'd do something like this:
Array1= [
{
id: 2,
city: 'washington',
code: 0099,
room: 5,
}
];
Array2= [
{
"id": 2,
"name": "john",
"number": 727625,
"etage": 5,
},
];
const result = Array1.filter(a1 => Array2.find(a2 => a1.id == a2.id) && Array2.find(a2 => a1.room == a2.etage));
console.log(result);
That will return a filtered array by room, etage and id.
You can use filter and some ES6 methods:
const arr1 = [
{
id: 1,
room: 1
},
{
id: 2,
room: 5
},
{
id: 3,
room: 3
}
];
const arr2 = [
{
id: 0,
etage: 0
},
{
id: 2,
etage: 5
},
{
id: 3,
etage: 3
}
];
const getTheSame = (arr1, arr2) => {
return arr1.filter(o1 =>
arr2.some(o2 => o1.id === o2.id && o1.room === o2.etage)
);
};
console.log("Result: ", getTheSame(arr1, arr2));

Trying to combine two datasets where object key ids match

I have two datasets and have been trying to combine them, but have no idea where to start. I am making two API calls and below is a small example of a response:
{
early_game_items: {
57: 16
59: 60
106: 1
180: 54
214: 28
232: 6
},
late_game_items: {
108: 1
116: 1
},
mid_game_items: {
1: 52
102: 3
108: 4
116: 1
193: 1
194: 1
223: 1
232: 73
}
}
The other data set is numbered from 1 - 300ish and is an object made of other objects. Below is an snippet:
const items = [{
"id": 57,
"name": "void_stone",
"cost": 825,
"secret_shop": 1,
"side_shop": 0,
"recipe": 0,
"localized_name": "Void Stone"
},
{
"id": 58,
"name": "mystic_staff",
"cost": 2700,
"secret_shop": 1,
"side_shop": 0,
"recipe": 0,
"localized_name": "Mystic Staff"
},
{
"id": 59,
"name": "energy_booster",
"cost": 900,
"secret_shop": 1,
"side_shop": 0,
"recipe": 0,
"localized_name": "Energy Booster"
}...]
I need to put the data from the second data set into the first by matching the key in the first data set with the id in the second. For example:
{
early_game_items: {
57: {amount: 16, name: 'void_stone', cost: 825}
59: {amount: 60...
106: {amount: 1...
180: {amount: 54...
214: {amount: 28...
232: {amount: 6...
}...
Thank you so much for looking this over! I am new to js and am really trying to learn.
Such as another answer naming the datasets dataset1 & dataset2, and assuming dataset2 is an array of objects. If dataset2 is big array, this answer has better performance:
let result = {};
// key = 'early_game_items', 'mid_game_items', 'late_game_items'
for(let key in dataset1) {
result[key] = {};
for(let id in dataset1[key]) {
result[key][id] = { amount: dataset1[key][id] };
}
}
for(let id in dataset2) {
for(let key in dataset1) {
let _item;
if(_item=result[key][id]){
const { name, cost } = dataset2[id];
_item.name = name;
_item.cost = cost;
}
}
}
console.log(result);
Naming the datasets dataset1 & dataset2, and assuming dataset2 is an array of objects:
let result = {};
// key = 'early_game_items', 'mid_game_items', 'late_game_items'
for(let key in dataset1) {
const itemGroup = dataset1[key];
let _itemGroup = {};
for(let id in itemGroup) {
let _item = { amount: itemGroup[id] };
// find item by id, in second dataset
const item = dataset2.find(i => i.id == id) || {};
// get name & cost via destructuring
const { name, cost } = item;
_item.name = name;
_item.cost = cost;
// store in new itemGroup
_itemGroup[id] = _item;
}
// store in result
result[key] = _itemGroup
}
console.log(result);
If dataset2 is an object with numbers as keys, you'll need to modify the "find by id" function:
// find item by id, in second dataset
const item = Object.values(dataset2).find(i => i.id === id) || {};

Javascript - How to sum the values in such an array?

I have such an array:
let array = {
[1]: {
name: 'test 1',
count: 5
},
[2]: {
name: 'test 2',
count: 3
}
}
How can I sum the values in the "count" column? Examples from simple arrays do not work. I currently have such a loop. Can it be done somehow better?
let sum = 0
Object.entries(array).forEach(([key, val]) => {
sum += val.count
});
Use reduce
let array = { 1: { name: "test 1", count: 5, }, 2: { name: "test 2", count: 3, }, };
total = Object.values(array).reduce((t, { count }) => t + count, 0); //t accumulator accumulates the value from previous calculation
console.log(total);
if you want to use a forEach loop like in your method use Object.values() instead because you only need values to calculate the sum of count
let array = {
1: { name: "test 1", count: 5 },
2: { name: "test 2", count: 3 },
};
let sum = 0;
Object.values(array).forEach(({ count }) => {
sum += count;
});
console.log(sum);
Building on top of the answer provided by #Sven.hig
Since you are calling the object "array" you might want to use an actual array instead.
Creating some functions to abstract away the complexity will help you understand your code better, when you come back to it in the future.
const add = (a, b) => a + b;
const sum = arr => arr.reduce(add, 0);
const data = [{
name: "test 1",
count: 5,
}, {
name: "test 2",
count: 3,
}
];
const total = sum(
data.map(d => d.count)
);
console.log(total);

Categorisation of objects by comparing two objects in javascript

I am trying to categorise the objects by comparing two objects say data and categories
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category":["A","1a", "2a"],
"B_category":["1b", "2b"],
"C_category":["1c", "2c"],
"D_category":["1d", "2d"]
};
I want to group the data based on the category object, when there is no match the group should be others and the resultant data should be like
const resultData = [
{ group: 'Others', name: '777', count: 456 },
{ group: 'A_category', name: '1a', count: 154 },
{ group: 'B_category', name: '1b', count: 765 },
{ group: 'C_category', name: '1c', count: 7877 }
]
I used the function but not able to achieve the result
const resultData = [];
function restructure(data, categories) {
Object.keys(data).map(
dataKey => {
for (let [key, value] of Object.entries(categories)) {
value.includes(dataKey) ? resultData.push({"group": key,...data[dataKey]}) : resultData.push({"group": "Others",...data[dataKey]}) ;
break;
}
}
)
}
restructure(data,categories);
You can try this as well. Iterate over your data entries and find whether the key exists in any of the categories object data and push it into the array with found category as group or push it with Others as group as shown in the below code
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category": ["A", "1a", "2a"],
"B_category": ["1b", "2b"],
"C_category": ["1c", "2c"],
"D_category": ["1d", "2d"]
};
const resultData = [];
Object.entries(data).map(([key, val])=>{
let group = Object.keys(categories).find(category=>categories[category].includes(key)) || 'Others'
resultData.push({
group,
...val
})
})
console.log(resultData)
Instead of for loop you need to use filter as let category = Object.entries(categories).filter(([key, value]) => value.includes(dataKey));.
If category.length > 0 then category is available else use Others.
Try it below.
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category": ["A", "1a", "2a"],
"B_category": ["1b", "2b"],
"C_category": ["1c", "2c"],
"D_category": ["1d", "2d"]
};
const resultData = [];
function restructure(data, categories) {
Object.keys(data).map(
dataKey => {
let category = Object.entries(categories)
.filter(([key, value]) => value.includes(dataKey));
resultData.push({
"group": category.length > 0 ? category[0][0] : "Others",
...data[dataKey]
});
})
}
restructure(data, categories);
console.log(resultData);
That's because you're breaking out of the loop regardless of whether you found the category or not. Your for loop will only execute once then breaks immediately. If the first category object matches, it is used, if not "Others" is assigned and the loop exits without checking the rest of the categories. Only break out of the loop if the lookup is successful:
for (let [key, value] of Object.entries(categories)) {
if(value.includes(dataKey)) { // if this is the category
resultData.push({ "group": key, ...data[dataKey] }); // use it ...
return; // ... and break the loop and the current iteration of forEach. The current object is handled
}
}
resultData.push({ "group": "Others", ...data[dataKey] }); // if the return statement above is never reached, that means the category was not found, assign "Others"
BTW, you can use other array methods to shorten things out like so:
function restructure(data, categories) {
return Object.keys(data).map(key => ({
"group": Object.keys(categories).find(cat => categories[cat].includes(key)) || "Others",
...data[key]
}));
}
Then use like so:
const resultData = restructure(data, categories);
My method uses find to try to find a category key that contains the name of the object, if find fails, it returns null at which point, the || "Others" part is evaluated and "Others" will be used as the group name (Does JavaScript have "Short-circuit" evaluation?).
Demo:
const data = {"777":{"name":"777","count":456},"1a":{"name":"1a","count":154},"1b":{"name":"1b","count":765},"1c":{"name":"1c","count":7877}};
const categories = {"A_category":["A","1a","2a"],"B_category":["1b","2b"],"C_category":["1c","2c"],"D_category":["1d","2d"]};
function restructure(data, categories) {
return Object.keys(data).map(key => ({
"group": Object.keys(categories).find(cat => categories[cat].includes(key)) || "Others",
...data[key]
}));
}
const resultData = restructure(data, categories);
console.log(resultData);

Categories

Resources