Related
The below script outputs
================================================
Log entry ID: 1
UUID: 2
Timestamp: 3
--------------------
Log entry ID: 4
UUID: 5
Timestamp: 6
which is what I want.
Right now description is hard coded, where I would like it to be built using arr instead.
My current thinking were to somehow generate the inner array in the map() function:
[
`Log entry ID: ${element['_id']}`,
`UUID: ${element['_source'].uuid}`,
`Timestamp: ${element['_source']['#timestamp']}\n`,
]
but because of the templating, this is not even a valid array with 3 elements, when looking at it on its own. So I am all out of ideas.
Question
Somehow I have to loop over the elements in arr before it is given to map(), I suppose.
Does anyone know how that could be done?
const dedent = require('dedent');
const arr = [
[ 'Log entry ID', '_id' ],
[ 'UUID', 'uuid' ],
[ 'Timestamp', '#timestamp' ],
]
;
const docs = [
{'_id': 1,'_source': {'uuid': 2,'#timestamp': 3}},
{'_id': 4,'_source': {'uuid': 5,'#timestamp': 6}},
];
const description = dedent`
================================================
${
docs.map((element) => [
`Log entry ID: ${element['_id']}`,
`UUID: ${element['_source'].uuid}`,
`Timestamp: ${element['_source']['#timestamp']}\n`,
].join('\n')).join('--------------------\n')
}
`;
console.log(description);
Update
I control arr so changing it to eg. is possible, or something else
const arr = [
[ 'Log entry ID', '_id' ],
[ 'UUID', {'_source': 'uuid'} ],
[ 'Timestamp', {'_source': '#timestamp'} ],
]
;
Since arr is in your control perhaps you can specify the path of the key itself.
const arr = [
['Log entry ID', '_id'],
['UUID', '_source.uuid'],
['Timestamp', '_source.#timestamp'],
['Description', '_source._desc']
];
const docs = [{
'_id': 1,
'_source': {
'uuid': 2,
'#timestamp': 3,
'_desc': 'test 1'
}
},
{
'_id': 4,
'_source': {
'uuid': 5,
'#timestamp': 6,
'_desc': 'test 2'
}
},
];
const getValue = (object, keys) => keys.split('.').reduce((o, k) => (o || {})[k], object);
console.log(docs.map((element) => arr.map((label) => {
return `${label[0]}: ${getValue(element, label[1])}`
}).join('\n')).join('\n--------------------\n'))
Assuming that the keys of a docs array element are all unique, one could traverse the object looking for a matching key.
function findVal(object, key) {
var value;
Object.keys(object).some(function(k) {
if (k === key) {
value = object[k];
return true;
}
if (object[k] && typeof object[k] === 'object') {
value = findVal(object[k], key);
return value !== undefined;
}
});
return value;
}
docs.map((element) =>
arr.map(([item, key]) =>
`${item}: ${findVal(element, key)}`)
)
The FindVal is taken from here.
you could create a function to get data from arr and put in description
/* Simple Hello World in Node.js */
const arr = [
[ 'Log entry ID', '_id' ],
[ 'UUID', 'uuid' ],
[ 'Timestamp', '#timestamp' ],
]
;
const docs = [
{'_id': 1,'_source': {'uuid': 2,'#timestamp': 3}},
{'_id': 4,'_source': {'uuid': 5,'#timestamp': 6}},
];
const findInArr=function(value){
var t = ""
arr.forEach(item=>{
if (item.includes(value)) {t = item[0]}
})
return t
}
const description = dedent`
================================================
${
docs.map((element) => [
`${findInArr('_id')}: ${element['_id']}`,
`${findInArr('UUID')}: ${element['_source'].uuid}`,
`${findInArr('Timestamp')}: ${element['_source']['#timestamp']}\n`,
].join('\n')).join('--------------------\n')
}`;
console.log(description);
i have an array A
const arrayA = [
{
id:a,
check:false
},
{
id:b,
check:false
},
{
id:c,
check:false
}
and an array B
const arrayB = [
{
id:a,
},
{
id:b,
}
]
and i want to check if arrayB is exist arrayA by id, then change check to true. Using lodash or js array methods
Hopefully I understood your question correctly but this is the solution I came up with.
arrayA.map((item) => ({ ...item, check: arrayB.some(({ id: idB }) => item.id === idB ) }))
You can use nested forEach loops, and check, if id matches then set check to true.
const arrayA = [{
id: "a",
check: false
},
{
id: "b",
check: false
},
{
id: "c",
check: false
}
]
const arrayB = [{
id: "a",
},
{
id: "b",
}
]
arrayB.forEach((b)=>{
arrayA.forEach((a)=>{
if(b.id == a.id){
a.check = true;
}
})
})
console.log(arrayA);
You could create an array containing the ids of arrayB and then check the objects in arrayA like
const arrayA = [
{
id: 'a',
check:false
},
{
id:'b',
check:false
},
{
id:'c',
check:false
} ];
const arrayB = [
{
id:'a',
},
{
id:'b',
}
];
const idsB = arrayB.map( obj => obj.id);
arrayA.forEach(obj => { if(idsB.indexOf(obj.id) > -1) obj.checked = true; } );
arrayA.forEach(obj => {console.log(JSON.stringify(obj))});
I could come up with this, which is not different than double loop, but may read easier.
arrayA.map((a) => {
a.check = arrayB.findIndex((b) => b.id === a.id) != -1;
return a;
});
Try this code it may help you
const arrayA = [
{id:'a',check:false},
{id:'b',check:false},
{id:'c',check:false}
]
const arrayB = [
{id:'a',},
{id:'b',}
]
arrayB.map(i => {
return i.check = arrayA.find(item => i.id == item.id)?.check;
});
console.log(arrayB)
Suppose I have an array of object as,
const sampleArray = [ {name: 'Arthur',details: [ {"book":["harry pottar","lotr","something"]} ,{"book":["omega","beta","simba"]} ] },
{name: 'simmone',details: [ {"book":["ronjan","pirates"]} ,{"book":["musical","eskobar","elsa"]} ]} ]
I want output as based on total length of book, sort object.
O/P: [ {name: 'simmone',details: [ {"book":["ronjan","pirates"]} ,{"book":["musical","eskobar","elsa"]} ]},
{name: 'Arthur',details: [ {"book":["harry pottar","lotr"]} ,{"book":["omega","beta","simba"]} ]} ]
For this I calculated the total length of each book, as,
const bookDetails = sampleArray.map(i => i.book);
const emptyArray = bookDetails.map(
subarr => subarr.reduce((a, b) => a + b.book.length, 0)
);
It gives me the length of book as [6,5], on basis of book count how can I sort the object as:
O/P: [ {name: 'simmone',details: [ {"book":["ronjan","pirates"]} ,{"book":["musical","eskobar","elsa"]} ]},
{name: 'Arthur',details: [ {"book":["harry pottar","lotr"]} ,{"book":["omega","beta","simba"]} ]} ]
If anyone needs any further information, please let me know
You could use customized sort to achieve this result. But it is highly inefficient because it is calculating the length of the books each and every time when customizing algorithm place element before and after. Better optimized version added below.
const sampleArray = [{
name: "Arthur",
details: [{
book: ["harry pottar", "lotr", "something"]
},
{
book: ["omega", "beta", "simba"]
},
],
},
{
name: "simmone",
details: [{
book: ["ronjan", "pirates"]
},
{
book: ["musical", "eskobar", "elsa"]
},
],
},
];
function getBookLength(arr) {
return arr.reduce((acc, curr) => acc + curr.book.length, 0);
}
const result = sampleArray.sort((a, b) => {
return getBookLength(a.details) - getBookLength(b.details);
});
console.log(result);
FOR BETTER PERFORMANCE: It would be better to store the length of the array so that we don't have to calculate each and every time
const sampleArray = [
{
name: "Arthur",
details: [
{ book: ["harry pottar", "lotr", "something"] },
{ book: ["omega", "beta", "simba"] },
],
},
{
name: "simmone",
details: [
{ book: ["ronjan", "pirates"] },
{ book: ["musical", "eskobar", "elsa"] },
],
},
];
const lengthDict = sampleArray.reduce((acc, { name, details }) => {
const length = details.reduce((a, b) => a + b.book.length, 0);
acc[name] = length;
return acc;
}, {});
const result = sampleArray.sort((a, b) => {
return lengthDict[a.name] - lengthDict[b.name];
});
console.log(result);
If you can do that, then you got everything you want, but you need to understand Sorting algorithm. I see that you didn't specify the direction of the sorting, like less books first, or more book first. But suppose we need More Books first:
const sortedArray = array.sort((elem1, elem2) => {
// Return less than 0 to keep elem1 before elem2
// Return greater than 0 to put elem2 before elem1
// Return 0 to keep as is, regarding the rest of elements
if (elem1.length > elem2.length) {
return -1;
} else if (elem1.length < elem2.length) {
return 1;
}
return 0;
});
Apply your reduce algorithm and you're good. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
this way :
const sampleArray =
[ { name: 'Arthur', details:
[ { book: [ 'harry pottar', 'lotr', 'something'] }
, { book: [ 'omega', 'beta', 'simba'] }
] }
, { name: 'simmone', details:
[ { book: [ 'ronjan', 'pirates'] }
, { book: [ 'musical', 'eskobar', 'elsa'] }
] } ]
const booksCnt =({details})=>details.reduce((s,{book})=>s+book.length ,0)
sampleArray.sort((a,b)=>booksCnt(a)-booksCnt(b))
console.log( sampleArray )
.as-console-wrapper {max-height: 100%!important;top:0;}
With the decpk optimization :
const sampleArray =
[ { name: 'Arthur', details:
[ { book: [ 'harry pottar', 'lotr', 'something'] }
, { book: [ 'omega', 'beta', 'simba'] }
] }
, { name: 'simmone', details:
[ { book: [ 'ronjan', 'pirates'] }
, { book: [ 'musical', 'eskobar', 'elsa'] }
] } ]
const booksCnt = ({details})=>details.reduce((s,{book})=>s+book.length ,0)
const NamesLens = Object.fromEntries(sampleArray.map(el=>([el.name,booksCnt(el)])) )
sampleArray.sort((a,b)=>NamesLens[a.name]-NamesLens[b.name] )
console.log( sampleArray )
.as-console-wrapper {max-height: 100%!important;top:0;}
I'm looking to filter in two deep arrays, actually my JSON:
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
},
"1": {
"product":[{
"uuid":"uid",
"name":"Milk"
}]
}
}
I would like to get something like that when I filter with the word "ric":
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
}]
}
}
But I got this result:
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
}
}
My code:
dataSort.categories = the json and
event.target.value.toLowerCase() = the specific word
dataSort.categories.filter(s => s.products.find(p => p.name.toLowerCase().includes(event.target.value.toLowerCase())));
You can achieve this with a combination of reduce and filter
var input = {
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
},
"1": {
"product":[{
"uuid":"uid",
"name":"Milk"
}]
}
}
var search = "ric"
var result = Object.entries(input).reduce( (acc, [key,val]) => {
found = val.product.filter(x => x.name.toLowerCase().includes(search.toLowerCase()))
if(found.length){
acc[key] = {...val, product: found}
}
return acc
},{})
console.log(result)
There is many approach to do this, one is to map your top level array to the subArrays filtered results then filter it after:
dataSort.categories
.map(s => s.products.filter(p => p.name.toLowerCase().includes(event.target.value.toLowerCase())))
.filter(s => !!s.products.length);
You may also prefer to get a "flat" array as result because it is easier to use after :
dataSort.categories
.reduce((acc, s) => [...acc, s.products.filter(p => p.name.toLowerCase().includes(event.target.value.toLowerCase()))], []);
Please find below the code to filter out values inside the product.name and only return the value which are matching the equality condition in product array.
const json = [
{
product: [
{
uuid: "uid",
name: "Rice",
},
{
uuid: "uid",
name: "Pasta",
},
],
},
{
product: [
{
uuid: "uid",
name: "Milk",
},
],
},
];
const inputValue = "rIc";
const filteredArray = [];
json.map((s) => {
const item = s.product.find((p) =>
p.name.toLowerCase().includes(inputValue.toLowerCase())
);
item && filteredArray.push({ product: item });
});
console.dir(filteredArray);
Your dataset is an Object, not an Array and the filter is an Array method. You can use reduce by looping on the object values by Object.values then filter your products array.
const data = {
'0': {
product: [
{
uuid: 'uid',
name: 'Rice',
},
{
uuid: 'uid',
name: 'Pasta',
},
],
},
'1': {
product: [
{
uuid: 'uid',
name: 'Milk',
},
],
},
};
const keyword = 'ric';
const dataset = Object.values(data);
const results = dataset.reduce((acc, item, index) => {
const search = keyword.toLowerCase();
const product = item.product.filter(product => product.name.toLowerCase().includes(search));
if (product.length) acc[index] = { ...item, product };
return acc;
}, {});
console.log(results);
I've got some data that came from classy service in Angular that looks like this (briefly):
const obj = {
field: [
{
id: 1,
items: []
},
{
id: 2,
items: [ { wateva: 'wateva1' } ]
},
{
id: 3,
items: false
},
{
id: 4,
items: [ { yeah: 7 } ]
}
]
}
Well, my task is just to collect all array items, that are not empty.
My solution (actually my solution is written in TypeScript and Angular 5, but here to make it more simple and comprehensible it's going to be like...) :
function getItems() {
const items = [];
obj.field.forEach(currentField => {
if (currentField.items && currentField.items.length) {
currentField.items.forEach(currentItem => items.push(currentItem));
}
});
return items;
}
Right, it's dead simple and it works as expected (current one will return...) :
[ { wateva: 'wateva1' }, { yeah: 7 } ]
And now my question... How to make my solution functional? I want to get rid of my new variable items, I don't want to push in that variable, I just want to return the result in one action. Any help will be appreciated.
P.S. Suggestions with 3rd libraries are not accepted :)
If you can use es6 (and since you mentioned you're using typescript, that should be fine), you can turn this into a nice functional one-liner by combining concat, map, filter, and the spread operator:
const obj = {
field: [
{
id: 1,
items: []
},
{
id: 2,
items: [ { wateva: 'wateva1' } ]
},
{
id: 3,
items: false
},
{
id: 4,
items: [ { yeah: 7 } ]
}
]
}
function getItems(obj) {
return [].concat(...obj.field.map(o => o.items).filter(Array.isArray))
}
console.log(getItems(obj))
You can use flatMap (stage 3). flatMap here matches Fantasy Land's spec for chain.
data.field.flatMap
(({ items }) =>
Array.isArray (items) ? items : []
)
// [ { wateva: 'wateva1' }, { yeah: 7 } ]
You can polyfill it in environments that don't have it
Array.prototype.flatMap = function (f) {
return this.reduce
( (acc, x) =>
acc.concat (f (x))
, []
)
}
Full program demonstration
Array.prototype.flatMap = function (f) {
return this.reduce
( (acc, x) =>
acc.concat (f (x))
, []
)
}
const data =
{ field:
[ { id: 1, items: [] }
, { id: 2, items: [ { wateva: 'wateva1' } ] }
, { id: 3, items: false }
, { id: 4, items: [ { yeah: 7 } ] }
]
}
const result =
data.field.flatMap
(({ items }) =>
Array.isArray (items) ? items : []
)
console.log (result)
// [ { wateva: 'wateva1' }, { yeah: 7 } ]
You can use Array.reduce and the spread operator to accumulate onto an empty array:
obj.field.reduce(
(acc, current) => current.items && current.items.length > 0 ? [...acc, ...current.items] : acc, [])
);
Using Array.prototype.reduce, object destructuring, and spread assignments:
function getItems({ field }) {
return field.reduce((result, { items }) =>
items instanceof Array ?
items.reduce((items, item) => [...items, item], result) :
result
, []);
}