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 }
]
}
*/
How can I sort an array by string?
const filter = [
{ index: "First" },
{ index: "Second" },
{ index: "Third" }
]
const data = [
{ title: 'Apple', index: "Second" },
{ title: 'Samsung', index: "First" },
{ title: 'BMW', index: "Third" },
{ title: 'Apple', index: "Second" },
{ title: 'Apple', index: "Second" },
{ title: 'Samsung', index: "First" }
]
Expected results:
const data = [
{ title: 'Samsung', index: "First" },
{ title: 'Samsung', index: "First" },
{ title: 'Apple', index: "Second" },
{ title: 'Apple', index: "Second" },
{ title: 'BMW', index: "Third" }
]
How to iterate over two arrays correctly?
Do I have to iterate over two arrays to do this? Or is there another way to get the desired result?
Given three inputs - "First", "Second", "Third", if there will be more of these indices?
Using Array#reduce, iterate over filter and save each object's index attribute and its index in the array in a Map
Using Array#sort, sort data by the values of the index attributes from the above map
const
filter = [ { index: "First" }, { index: "Second" }, { index: "Third" } ],
data = [ { title: 'Apple', index: "Second" }, { title: 'Samsung', index: "First" }, { title: 'BMW', index: "Third" }, { title: 'Apple', index: "Second" }, { title: 'Apple', index: "Second" }, { title: 'Samsung', index: "First" } ];
const indexSortMap = filter.reduce((map, { index }, i) => map.set(index, i), new Map);
data.sort(({ index: a }, { index: b }) => indexSortMap.get(a) - indexSortMap.get(b));
console.log(data);
Use Array#sort with custom compareFunction and Array#findIndex with custom callback function
const filter = [ { index: "First",}, {index: "Second",}, {index: "Third",}];
const data = [ { title: 'Apple', index: "Second" }, { title: 'Samsung', index: "First" }, { title: 'BMW', index: "Third" }, { title: 'Apple', index: "Second" }, { title: 'Apple', index: "Second" }, { title: 'Samsung', index: "First" } ];
data.sort((a ,b) => {
return filter.findIndex(i => i.index === a.index) - filter.findIndex(i => i.index === b.index);
});
console.log(data);
One possible solution (and simple) is by iterating these two arrays. They will be pushed according to the order of the "filter" array:
const sortedArray = []
filter.forEach(fil=> {
data.forEach(dat=>{
if(dat.index===fil.index)
sortedArray.push(dat)
})
})
console.log(sortedArray)
One approach would be to push the objects to three different arrays, each one for an index and then concatenate the arrays.
var arr1 = [],arr2 = [],arr3 = [], data = [ { title: 'Apple', index: "Second" }, { title: 'Samsung', index: "First" }, { title: 'BMW', index: "Third" }, { title: 'Apple', index: "Second" }, { title: 'Apple', index: "Second" }, { title: 'Samsung', index: "First" } ];
for(d of data) d.index == 'First' ? arr1.push(d) : d.index == 'Second' ? arr2.push(d) : arr3.push(d);
data = [].concat.apply([], [arr1, arr2, arr3]);
console.log(data);
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.
I have an object I want to filter so that trainers with the most electric-type pokemon are listed first, but trainers without any electric-type pokemon are still present (represented as an empty array)
Here's my object:
obj = {
trainer1: [
{ name: 'pikachu', type: 'electric', id: 25 },
{ name: 'zapdos', type: 'electric', id: 145 },
{ name: 'psyduck', type: 'water', id: 54 },
],
trainer2: [
{ name: 'eevee', type: 'normal', id: 133 },
{ name: 'magmar', type: 'fire', id: 126 }
],
trainer3: [
{ name: 'ditto', type: 'normal', id: 132 },
{ name: 'magnemite', type: 'electric', id: 81 }
]
}
Becomes this object:
obj = {
trainer1: [
{ name: 'pikachu', type: 'electric', id: 25 },
{ name: 'zapdos', type: 'electric', id: 145 }
],
trainer3: [
{ name: 'magnemite', type: 'electric', id: 81 }
]
trainer2: [] // Array still present, but empty
}
I know reduce would come in handy here but I'm not sure how to set it up correctly.
This may be the bruteforce solution and there will be better solution than this but i think you can do it like the following way.
const tempArr = Object.keys(obj).map(key=>{
return {
key:key,
value:obj[key].filter(pokemon=>pokemon.type==='electric')
}
})
let newObj = {}
tempArr.sort((a,b)=>b.value.length-a.value.length)
tempArr.forEach(item=>{
newObj[item.key] = item.value
})
console.log(newObj)
I have an array:
[
{
meta:'abc'
data:
{
name:'asd',
title: 'bbb'
}
},
{
meta:'abc'
data:
{
name:'asd',
title: 'bbb'
},
{
meta:'abc'
data:
{
name:'asd',
title: 'bbb'
}
}
]
I want to convert it to new array, which will be:
[
{
name:'asd',
title: 'bbb'
},
{
name:'asd',
title: 'bbb'
{
name:'asd',
title: 'bbb'
}
]
I want to take only the data elements, and to create from them a new array.
How can I do it the fastest way? And how can I do it with lodash?
Thanks!
You can use _.map
var array = [{
meta: 'abc',
data: {
name: 'asd',
title: 'bbb'
}
}, {
meta: 'abc',
data: {
name: 'asd',
title: 'bbb'
}
}, {
meta: 'abc',
data: {
name: 'asd',
title: 'bbb'
}
}];
console.log(_.map(array, 'data'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.2.0/lodash.js"></script>
Just you need is the map() native javascript method to arrays:
//considere that 'someArray' is the same as your.
var newArray = someArray.map(function(item){
return {name: item.data.name, title: item.data.title};
});