My array looks like, but includes 1000+ objects:
data = {
0: {
code: '3019476',
_id: '60033f61-8a4e-4622-9731-decd07bc44e1',
vendor: 'DKNY',
tags: [
{ type: 1, label: 'dsfs' },
{ type: 2, label: 'thisOne' },
{ type: 3, label: 'sdas' },
],
},
1: {
code: '3019475',
_id: '60033f61-8a4e-4622-9731-decd07bc44e0',
vendor: 'DKNY',
tags: [
{ type: 1, label: 'sdfsd' },
{ type: 2, label: 'anotherOne' },
{ type: 3, label: 'sdfsd' },
],
},
2: {
code: '3019474',
_id: '60033f61-8a4e-4622-9731-decd07bc44e9',
vendor: 'DKNY',
tags: [
{ type: 1, label: 'gregf' },
{ type: 2, label: 'thisOne' },
{ type: 3, label: 'gregf' },
],
},
};
I expect this as output:
0: {title: "thisOne", data: Array(2)"}
1: {title: "anotherOne", data: Array(1)"}
So I want to filter and count how many objects include different tag[1].label, saving full item data in 'data' inside result.
All my ideas failed, so I really need your help
I suspect that you are looking for groupBy with a property path iteratee shorthand:
import { groupBy } from 'underscore';
groupBy(data, ['tags', 1, 'label']);
// { thisOne: [Object, Object],
// anotherOne: [Object]
// }
This produces an object with the labels as keys and the groups as values. From here, it is easy to get it in the exact shape of the example output from your question, for example using chain and map:
import { chain } from 'underscore';
chain(data)
.groupBy(['tags', 1, 'label'])
.map((data, title) => ({data, title}))
.value();
// [ { title: 'thisOne', data: [Object, Object] },
// { title: 'anotherOne', data: [Object] }
// ]
There are a lot of syntax error in data, fix them and it should work. See the code snippet below.
NOTE: Key/Value pairs in an object should separated with commas and dsfs, thisOne sdas should be valid variables or strings.
data = {
0: {
code: '3019476',
_id: '60033f61-8a4e-4622-9731-decd07bc44e1',
vendor: 'DKNY',
tags: [
{ type: 1, label: 'dsfs' },
{ type: 2, label: 'thisOne' },
{ type: 3, label: 'sdas' },
],
},
1: {
code: '3019475',
_id: '60033f61-8a4e-4622-9731-decd07bc44e0',
vendor: 'DKNY',
tags: [
{ type: 1, label: 'sdfsd' },
{ type: 2, label: 'anotherOne' },
{ type: 3, label: 'sdfsd' },
],
},
2: {
code: '3019474',
_id: '60033f61-8a4e-4622-9731-decd07bc44e9',
vendor: 'DKNY',
tags: [
{ type: 1, label: 'gregf' },
{ type: 2, label: 'thisOne' },
{ type: 3, label: 'gregf' },
],
},
};
console.log(data[0].tags[1].label)
You don't need lodash for that. You can use plain javascript.
const data= [
{code: "3019476", _id: "60033f61-8a4e-4622-9731-decd07bc44e1", vendor: "DKNY", tags: [{type: 1, label: 'dsfs'},{type: 2, label: 'thisOne'},{type: 3, label: 'sdas'}]}, {code: "3019475", _id: "60033f61-8a4e-4622-9731-decd07bc44e0", vendor: "DKNY", tags: [{type: 1, label: 'sdfsd'},{type: 2, label: 'anotherOne'},{type: 3, label: 'sdfsd'}]}, {code: "3019474", _id: "60033f61-8a4e-4622-9731-decd07bc44e9", vendor: "DKNY", tags: [{type: 1, label: 'gregf'},{type: 2, label: 'thisOne'},{type: 3, label: 'gregf'}]}]
const newData = data.map(d => {
let result = {};
result.title = d.tags.find(tag => tag.type === 2).label;
result.data = d.tags.filter(tag => tag.type === 2);
return result;
});
console.log(newData);
P.S.: This snippet is considering that more than one tagType can be included into the data array and that the first found will be used as title.
If that is not what you want then the question should be clearer.
Related
How do I implement this properly?
const tree = buildTree(1, shuffleArray([
{ type: 'string', source_id: 1, name: 'foo', value: 'asdf' },
{ type: 'integer', source_id: 1, name: 'bar', value: 123 },
{ type: 'object', source_id: 1, name: 'nested', value: 2 },
{ type: 'object', source_id: 2, name: 'nested', value: 3, array: true },
{ type: 'boolean', source_id: 3, name: 'random', value: true },
{ type: 'string', source_id: 3, name: 'another', value: 'hello' },
{ type: 'object', source_id: 2, name: 'nested', value: 4, array: true },
{ type: 'boolean', source_id: 4, name: 'random', value: false },
{ type: 'string', source_id: 4, name: 'another', value: 'world' },
{ type: 'object', source_id: 2, name: 'nested', value: 5, array: true },
{ type: 'boolean', source_id: 5, name: 'random', value: true },
{ type: 'string', source_id: 5, name: 'another', value: 'awesome' },
]))
function buildTree(startId, array) {
const map = array.reduce((m, x) => {
m[x.source_id] = m[x.source_id] ?? {}
if (x.array) {
m[x.source_id][x.name] = m[x.source_id][x.name] ?? []
m[x.source_id][x.name].push({ id: x.value })
} else {
m[x.source_id][x.name] = x.value
}
return m
}, {})
// ??? getting lost...
}
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array
}
where the "expected tree" would be something like this:
const expectedTree = {
id: 1,
foo: 'asdf',
bar: 123,
nested: {
id: 2,
nested: [
{
id: 3,
random: true,
another: 'hello'
},
{
id: 4,
random: false,
another: 'world'
},
{
id: 5,
random: true,
another: 'awesome'
}
]
}
}
The shuffleArray is just to show that the records could be in any order, and the id (source_id) property is not necessarily in incremental order (actually in my case they are UUIDs with the hierarchy not really in any particular order). Each "record" in buildTree is a "property" record basically like this:
create table object_properties {
uuid id;
uuid source_id; // the object which has this property
string name; // the property name
uuid value; // the property value object
}
// ...and same for boolean, integer, etc. properties
create table string_properties {
uuid id;
uuid source_id; // the object which has this property
string name; // the property name
string value; // the property value string
}
In my buildTree I can kind of imagine creating a map from the source_id (the base object node which has property name), to the names, to the values. But then maybe iterating over the source IDs, looking for objects nested inside the name values, and converting them to objects instead of just IDs. But this is getting hard to comprehend and I'm sure there is an easier way somehow.
What is an algorithm to build an "object tree" from this flat list of records?
In my situation, I am fetching a bunch of deeply nested property objects, recursively, and need to stitch back together an object tree out of them.
It looks like the name "nested" plays a special role. When it occurs, the corresponding value property does not hold a literal value to assign to the named property (as is the case with other names), but is a reference to an existing source_id value.
This means your code needs to deal with that name specifically and then establish the parent-child relationship. This relationship is further influenced by the array property.
I would define buildTree as follows, making use of a Map, which is built first using its constructor argument:
function buildTree(startId, arr) {
const map = new Map(arr.map(({source_id}) => [source_id, { id: source_id }]));
for (const {source_id, name, value, array} of arr) {
if (name !== "nested") {
map.get(source_id)[name] = value;
} else if (array) {
(map.get(source_id).nested ??= []).push(map.get(value));
} else {
map.get(source_id).nested = map.get(value);
}
}
return map.get(startId);
}
// Code below has not changed
function shuffleArray(array) { for (var i = array.length - 1, j, temp; i > 0; i--) {j = Math.floor(Math.random() * (i + 1));temp = array[i];array[i] = array[j];array[j] = temp;} return array;}
const tree = buildTree(1, shuffleArray([{ type: 'string', source_id: 1, name: 'foo', value: 'asdf' },{ type: 'integer', source_id: 1, name: 'bar', value: 123 },{ type: 'object', source_id: 1, name: 'nested', value: 2 },{ type: 'object', source_id: 2, name: 'nested', value: 3, array: true },{ type: 'boolean', source_id: 3, name: 'random', value: true },{ type: 'string', source_id: 3, name: 'another', value: 'hello' },{ type: 'object', source_id: 2, name: 'nested', value: 4, array: true },{ type: 'boolean', source_id: 4, name: 'random', value: false },{ type: 'string', source_id: 4, name: 'another', value: 'world' },{ type: 'object', source_id: 2, name: 'nested', value: 5, array: true },{ type: 'boolean', source_id: 5, name: 'random', value: true },{ type: 'string', source_id: 5, name: 'another', value: 'awesome' },]))
console.log(tree);
Note that the order in which objects are pushed into arrays is defined by the original order of the objects. Since this input array is shuffled, the output may show arrays in different ordering on separate runs. Something similar holds for object keys (see Object property order)
You should try Array.prototype.group(). Please refer below document.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/group
const inventory = [
{ name: 'asparagus', type: 'vegetables', quantity: 5 },
{ name: 'bananas', type: 'fruit', quantity: 0 },
{ name: 'goat', type: 'meat', quantity: 23 },
{ name: 'cherries', type: 'fruit', quantity: 5 },
{ name: 'fish', type: 'meat', quantity: 22 }
];
const result = inventory.group(({ type }) => type);
/* Result is:
{
vegetables: [
{ name: 'asparagus', type: 'vegetables', quantity: 5 },
],
fruit: [
{ name: "bananas", type: "fruit", quantity: 0 },
{ name: "cherries", type: "fruit", quantity: 5 }
],
meat: [
{ name: "goat", type: "meat", quantity: 23 },
{ name: "fish", type: "meat", quantity: 22 }
]
}
*/
There is an object and an array.
list: [
{
oldData: {
title: 'abc',
id: 1,
date: '1982-09-30',
budget: 250000,
},
newData: [
{
key: 1,
data: null,
value: 5,
},
{
key: 2,
data: null,
value: 22,
},
...
],
},
{
oldData: {
title: 'blablablaaaaa',
id: 2,
date: '2012-02-23',
budget: 350000,
},
newData: [
{
key: 1,
data: null,
value: 35,
},
{
key: 2,
data: null,
value: 41,
},
...
],
},
... some more datas...
]
as above, There is more data of the same type.
I need to use oldData and newData together, so I want to combine the two.
How can I combine oldData and newData so that there are multiple sets of oldData and newData pairs?
for example, [{ combineData: {...} }, { combineData: {...} }, ... }] here.
I know how to combine array and array, object and object, but I do not know how to do that.
Is there any good solution?
Your desired result is unclear, but you did say you wanted old/new pairs. This answer is different than the others in that it produces an array of combined data objects made of old/new pairs, where the oldData values are duplicated in order to appear alongside each corresponding newData value within its list item.
your original data after the first question update:
let list = [
{
oldData: { title: 'abc', id: 1, date: '1982-09-30', budget: 250000 },
newData: [
{ key: 1, data: null, value: 5 },
{ key: 2, data: null, value: 22 },
//...
],
},
{
oldData: { title: 'blablablaaaaa', id: 2, date: '2012-02-23', budget: 350000 },
newData: [
{ key: 1, data: null, value: 35 },
{ key: 2, data: null, value: 41 },
//...
],
},
//... some more datas...
];
This code maps each {old,new[]} list item into arrays of pairs [{old,new}, {old,new}, ...] that are combined in the final reduce() call:
var combinedDatas = list
.map(listItem => listItem.newData.map(newItem => ({
oldData: listItem.oldData,
newData: newItem
})))
.reduce();
console.log(JSON.stringify(oldNewCombos, null, 4));
produces a list of denormalized pairs:
[
{ list[0].oldData, list[0].newData[0] },
{ list[0].oldData, list[0].newData[1] },
//...rest of list[0] oldData with newData[n] combos
{ list[1].oldData, list[1].newData[0] },
{ list[1].oldData, list[1].newData[1] },
//...rest of list[1] oldData with newData[n] combos
{ list[2].oldData, list[2].newData[0] },
{ list[2].oldData, list[2].newData[1] },
//...rest of list[2] oldData with newData[n] combos
//...
]
You can use map() on the array. And use Object.assign() and spread operator to combine all the properties of all the elements in newData into one object.
const arr = [
{
oldData: {
a:10,
b:20
},
newData: [
{
c:30,
d:40
},
{
e:50,
f:60
}
],
}
]
const res = arr.map(x => ({combinedData:{...x.oldData, ...Object.assign({}, ...x.newData)}}))
console.log(res)
You can map over the array and use object destructuring (...object) to create a new, combined object:
const data = [
{
oldData: {
foo: 'lorem',
},
newData: {
bar: 'ipsum',
},
},
{
oldData: {
foo: 'dolor',
},
newData: {
bar: 'sit amet',
},
},
];
const combined = data.map(record => ({...record.oldData, ...record.newData}));
console.log(combined);
This will overwrite duplicate keys however, so something like:
{
oldData: {
message: 'a',
},
newData: {
message: 'b',
},
}
will become:
{
message: 'b',
}
I'm looking for method to find cartesian array based on array of objects.
Basicly I've seen solutions like that:
Cartesian product of multiple arrays in JavaScript
but I'm not sure how to modify it to work on object property (in my case on property "value").
For instance my input:
let arr1 = [
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 2,
type: "attribute",
value: "arr1-attr2"
}
];
let arr2 = [
{
id: 3,
type: "attribute",
value: "arr2-attr1"
}
];
let arr3 = [
{
id: 4,
type: "attribute",
value: "arr3-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
];
Expected output:
output = [
[
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 4,
type: "attribute",
value: "arr3-attr1"
}
],
[
{
id: 2,
type: "attribute",
value: "arr1-attr2"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
],
[
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 4,
type: "attribute",
value: "arr3-attr1"
}
],
[
{
id: 2,
type: "attribute",
value: "arr1-attr2"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
]
];
Just take the single arrays as items of a collection of this arrays and perform the algorithm on this data set.
var arr1 = [{ id: 1, type: "attribute", value: "arr1-attr1" }, { id: 2, type: "attribute", value: "arr1-attr2" }],
arr2 = [{ id: 3, type: "attribute", value: "arr2-attr1" }],
arr3 = [{ id: 4, type: "attribute", value: "arr3-attr1" }, { id: 5, type: "attribute", value: "arr3-attr2" }],
result = [arr1, arr2, arr3]
.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have the following JSON object:
var test = {
data: [{
itemID: 0,
categories: [{
id: 0,
type: 'a',
name: 'world'
}, {
id: 1,
type: 'b',
name: 'plants'
}]
},
{
itemID: 1,
categories: [{
id: 2,
type: 'w',
name: 'cars'
}, {
id: 3,
type: 't',
name: 'bicycles'
}]
}
]
};
console.log([].concat
.apply([], test.data.map(item => item.categories.map(el => el.type))));
What I want to do is, to get all types in an array.
So the result should look like this:
['a', 'b', 'w', 't']
What I did:
[].concat
.apply([], test.data.map(item => item.categories.map(el => el.type)))
I have the feeling that this could be done easier.
Does someone know a better solution ?
You can use Array.prototype.map() and Array.prototype.flat():
The flat() method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.
Where depth is Optional
The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
var test = {
data: [{
itemID: 0,
categories: [{
id: 0,
type: 'a',
name: 'world'
}, {
id: 1,
type: 'b',
name: 'plants'
}]
},
{
itemID: 1,
categories: [{
id: 2,
type: 'w',
name: 'cars'
}, {
id: 3,
type: 't',
name: 'bicycles'
}]
}
]
};
var type = test.data.map(item => item.categories.map(el => el.type)).flat();
console.log(type);
Use Array.reduce
var test = {data: [{itemID: 0,categories: [{id: 0,type: 'a',name: 'world'}, {id: 1,type: 'b',name: 'plants'}]},{itemID: 1,categories: [{id: 2,type: 'w',name: 'cars'}, {id: 3,type: 't',name: 'bicycles'}]}]};
let result = test.data.reduce((a,c) => a.concat(c.categories.map(v => v.type)), []);
console.log(result);
So, I get a JSON response from the server that looks something like:
{
data: [
{ id: 1, type: 'person', emails: [ { id: 1 }, { id: 3 } ], phones: [] },
{ id: 2, type: 'person', emails: [ { id: 2 } ], phones: [ { id: 2 } ] },
{ id: 3, type: 'person', emails: [ { id: 4 } ], phones: [ { id: 3 }, { id: 3 }] }
],
included: [
{ id: 1, type: 'emails', ... },
{ id: 2, type: 'emails', ... },
{ id: 3, type: 'emails', ... },
{ id: 4, type: 'emails', ... },
{ id: 1, type: 'phones', ... },
{ id: 2, type: 'phones', ... },
{ id: 3, type: 'phones', ... }
]
}
The data property is an array of contact objeccts all with the same structure. Each contact object has an array of related emails and phones.
The included property is an array of ALL types of related objects which means they can share and id or even have a difference object structure.
I'm looking to try and flatten the response to be easier to work with and resemble:
{
entities: {
contacts: [ ... ],
emails: [ ... ],
phones: [ ... ]
},
result: [ 1, 2, 3 ]
}
I've managed to normalize just the contact data using:
const contactSchema = new schema.Entity('contacts');
const contactListSchema = [ contactSchema ];
const normalizedData= normalize(response, contactListSchema);
But that obviously won't include the emails or phones in the entities.
I don't actually know if this library is capable of what I'm trying to achieve, but any help would be appreciated.
While not based on the data above, the API is based off of the jsonapi.org schema, so the example on the homepage matches exactly with the structure.
I actually found a library specifically designed to do this based on the original normalizr:
https://github.com/stevenpetryk/jsonapi-normalizer
Hope this may help someone in the future!