For example, in the following json array each element has a name and a variable list of items in tracks.items
[
{
"name": "whatever",
"tracks": {
"items":
[
{
"item 1" : "some item"
},
...
]
}
},
{...}
...
]
In javascript, I need to find the number of distinct name's, as well as the sum of the length (size) of all tracks.items found in the entire array.
How do I use underscore or lodash to do that?. I've tried _.countBy() but that only returns individual counts
EDIT
thanks guys
(alternate solution)
const uniqueNames = new Set(array.map(item => item.name)).size
const totalItems = array.reduce((sum, item) => sum + item.tracks.items.length, 0)
As Stucco noted in his answer you can get the number of unique names, by checking the length of the result of _.uniqBy() by name.
You can get the total amount of track items using _.sumBy().
var arr = [{"name":"whatever","tracks":{"items":[{"item 1":"some item"},{"item 2":"some item"},{"item 3":"some item"}]}},{"name":"whatever","tracks":{"items":[{"item 4":"some item"},{"item 5":"some item"},{"item 6":"some item"},{"item 7":"some item"}]}},{"name":"whatever2","tracks":{"items":[{"item 8":"some item"},{"item 9":"some item"},{"item 10":"some item"},{"item 11":"some item"}]}}];
var uniqueNamesCount = _.uniqBy(arr, 'name').length;
var totalTracksItemsCount = _.sumBy(arr, 'tracks.items.length');
console.log('uniqueNamesCount', uniqueNamesCount); // 2
console.log('totalTracksItemsCount', totalTracksItemsCount); // 11
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
let obj = [{
"name": "whatever",
"tracks": {
"items":
[
{
"item 1" : "some item"
},
...
]
}
},
{...}
...
];
let uniqueNames = _.uniqBy(obj, 'name').length;
let totalItemes = _.reduce(obj, (result, value) => result += value.tracks.items.length, 0);
Working example:
var obj = [{
"name": "whatever",
"tracks": {
"items": [{
"item 1": "some item"
},
{
"item 2": "some item"
}, {
"item 3": "some item"
}
]
}
}, {
"name": "whatever",
"tracks": {
"items": [{
"item 4": "some item"
},
{
"item 5": "some item"
}, {
"item 6": "some item"
}, {
"item 7": "some item"
}
]
}
}, {
"name": "whatever2",
"tracks": {
"items": [{
"item 8": "some item"
},
{
"item 9": "some item"
}, {
"item 10": "some item"
}, {
"item 11": "some item"
}
]
}
}]
let names = _.uniqBy(obj, 'name');
let uniqueNames = names.length
let items = _.reduce(obj, (result, value) => result += value.tracks.items.length, 0)
console.log(names);
console.log(uniqueNames)
console.log(items)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
Related
I have an object
const items = {
"123": {
"key": 123,
"name": "one name",
},
"456": {
"key": 456,
"name": "two name",
},
"789": {
"key": 789,
"name": "three name",
},
};
Need to filter this from below array, with array index as object.key
const myFilter = [123,789];
Code I am trying is as below but it returning array inedx as 0,1,2... but I need the index to be object.key.
let selectedItems = myFilter.map((key) => {
return items[key];
});
Current output :
[0:{
key: 123,
name: "one name"
}, 1: {
key: 789,
name: "three name"
}]
Expected Output
[123:{
key: 123,
name: "one name"
}, 789: {
key: 789,
name: "three name"
}]
jsfiddle - https://jsfiddle.net/kb374exh/2/
Your actual output is actually correct and the only possible result from mapping the myFilter array to the matching properties from items.
const items = {
"123": {
"key": 123,
"name": "one name",
},
"456": {
"key": 456,
"name": "two name",
},
"789": {
"key": 789,
"name": "three name",
},
};
const myFilter = [123, 789];
const selectedItems = myFilter.map((key) => items[key]);
console.log(selectedItems);
The logged output you see is including the array index. You likely are seeing the index included when logging in the browser.
If you want an array of objects where the original key is the new index then the best you can likely do is an array of length <highest key> and a bunch of "holes" that are simply undefined.
const items = {
"123": {
"key": 123,
"name": "one name",
},
"456": {
"key": 456,
"name": "two name",
},
"789": {
"key": 789,
"name": "three name",
},
};
const myFilter = [123, 789];
const selectedItems = Object.entries(items).reduce((selectedItems, [key, value]) => {
if (myFilter.includes(value.key)) selectedItems[key] = value;
return selectedItems;
}, []);
console.log(selectedItems);
If you are ok with the result being an object then you can have more succinct output, you'll basically end up with back with an object with key-value pairs filtered out.
const items = {
"123": {
"key": 123,
"name": "one name",
},
"456": {
"key": 456,
"name": "two name",
},
"789": {
"key": 789,
"name": "three name",
},
};
const myFilter = [123, 789];
const selectedItems = Object.fromEntries(Object.entries(items).filter(([, value]) => myFilter.includes(value.key)));
console.log(selectedItems);
I have the following JSON of structured, nested data.
[
{
"id": "2e270ad7-90aa-41da-bb57-a777448f5906",
"name": "First Level 1",
"childValues": [
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1 "
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
}
]
},
{
"id": "18889675-9d71-420e-84a6-3603af044b6c",
"name": "First Level 2",
"childValues": [
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
}
]
},
{
"id": "71113ffb-62f0-4d76-941f-974be3cd35cb",
"name": "First Level 3",
"childValues": [
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
}
]
I am trying to extract an array of objects from this nested array structure based on a level of nesting. For instance, level 0 just gives me back all of the base object in the array, but if I ask for level 1, I am trying to get back an output of just the second level nested objects, under the childValues property, in a single array, like the following:
[
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1 "
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
},
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
},
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
And if I ask for level 2, I should only get the third level objects:
[
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
The only thing I have figured out is how to completely flatten the structure recursively, but cant put my finger on how to extract a specific level.
private flat(array: any[]) {
let result: any[] = [];
array.forEach((a) => {
result.push(a);
if (Array.isArray(a.childValues)) {
result = result.concat(this.flat(a.childValues));
}
});
return result;
}
Here's a cleaner version of Shane Padgett's function:
const getArrayByNthLevel = (array, levelToGet, currentLevel=0) => array.reduce((retval, a) => {
levelToGet === currentLevel
? retval.push(a)
: Array.isArray(a.childValues)
? retval = retval.concat(getArrayByNthLevel(a.childValues, levelToGet, currentLevel + 1))
: false
return retval;
}, []);
I was able to achieve this with the following function.
function getArrayByNthLevelOfPropName(array, propName, levelToGet, currentLevel = 0) {
let result = [];
array.forEach((a) => {
if (levelToGet === currentLevel) {
result.push(a);
}
if (Array.isArray(a[propName]) && levelToGet !== currentLevel) {
result = result.concat(getArrayByNthLevelOfPropName(a[propName], propName, levelToGet, ++currentLevel));
currentLevel -= 1;
}
});
return result;
}
You can use flatMap like this:
const input=[{id:"2e270ad7-90aa-41da-bb57-a777448f5906",name:"First Level 1",childValues:[{id:"4cecbd28-fd06-4c2a-9b57-33d4a298675c",name:"Second Level 1 "},{id:"09893799-e21c-498f-96b4-e63e366a3c18",name:"Second Level 2"}]},{id:"18889675-9d71-420e-84a6-3603af044b6c",name:"First Level 2",childValues:[{id:"b7093ca1-5fed-4eb2-b934-637bfdc6c7da",name:"Second Level 3"},{id:"a3575212-1746-4dd3-ab52-4e37786c035c",name:"Second Level 4"}]},{id:"71113ffb-62f0-4d76-941f-974be3cd35cb",name:"First Level 3",childValues:[{id:"160570a5-29aa-4fdb-bb16-d9d7637d0177",name:"Second Level 5",childValues:[{id:"2df28cb9-9ac4-478c-a2a4-6dc5206c983b",name:"Third Level 1"},{id:"a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",name:"Third Level 2"}]},{id:"6e5947ea-2c47-4d2b-8ecd-6369c728e7db",name:"Second Level 6"}]}];
const getLevel = (arr = [], required, current = 0) =>
required === current
? arr
: arr.flatMap(a => getLevel(a.childValues, required, current + 1))
console.log("Second Level: \n ", getLevel(input, 1))
console.log("Third Level: \n ", getLevel(input, 2))
If flatMap is not supported, you can use
[].concat(...arr.map(a => getLevel(a.childValues, required, current + 1)))
Following should work:
var data = [
{
"id": "2e270ad7-90aa-41da-bb57-a777448f5906",
"name": "First Level 1",
"childValues": [
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1"
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
}
]
},
{
"id": "18889675-9d71-420e-84a6-3603af044b6c",
"name": "First Level 2",
"childValues": [
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
}
]
},
{
"id": "71113ffb-62f0-4d76-941f-974be3cd35cb",
"name": "First Level 3",
"childValues": [
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
}
];
function getData(data, targetLevel, currentLevel = 0) {
// If this is the target level, then extract the
// data we need from each item, and return the array
if (currentLevel == targetLevel) {
return data;
}
// Otherwise, run a map over the items, and if they have
// 'childValues', then recurs, but increment the value of
// 'current level' it will be iterating on
// Because 'map' will return array of array, merge them
// to a single array
return [].concat(...data.map(item => {
if (item.childValues) {
return getData(item.childValues, targetLevel, currentLevel + 1);
};
return [];
}));
}
document.getElementById("dataLevel0").innerHTML = JSON.stringify(getData(data, 0), null, 4);
document.getElementById("dataLevel1").innerHTML = JSON.stringify(getData(data, 1), null, 4);
document.getElementById("dataLevel2").innerHTML = JSON.stringify(getData(data, 2), null, 4);
<div>
Level 1:
</div>
<pre id="dataLevel0">
</pre>
<div>
Level 2:
</div>
<pre id="dataLevel1">
</pre>
<div>
Level 3:
</div>
<pre id="dataLevel2">
</pre>
I would like to find number of items which has specific text using js filter method.
var num =
[
{
"name": "name1 ",
"category": "test"
},
{
"name": " name2",
"category": "test2"
},
{
"name": "name3",
"category": "cat3"
},
{
"name": "name 4",
"category": "cat 4"
}
];
num is an object;
Now, i want to find number of categories which has text 'cat'. So i want the result 2. How to get that using filter method.
here's how you can do it
var num =
[
{
"name": "name1 ",
"category": "test"
},
{
"name": " name2",
"category": "test2"
},
{
"name": "name3",
"category": "cat3"
},
{
"name": "name 4",
"category": "cat 4"
}
];
console.log( num.filter(i => i.category.indexOf("cat") === 0).length )
num is an object;
True, but specifically it's an array object.
You could use filter for this, but reduce would be the more appropriate option if you don't want the array of matching results:
var result = num.reduce(function(sum, entry) => sum + (entry.category.includes("cat") ? 1 : 0), 0);
Live Example:
var num =
[
{
"name": "name1 ",
"category": "test"
},
{
"name": " name2",
"category": "test2"
},
{
"name": "name3",
"category": "cat3"
},
{
"name": "name 4",
"category": "cat 4"
}
];
var result = num.reduce(function(sum, entry) {
return sum + (entry.category.includes("cat") ? 1 : 0);
}, 0);
console.log(result);
Or with ES2015+:
const num =
[
{
"name": "name1 ",
"category": "test"
},
{
"name": " name2",
"category": "test2"
},
{
"name": "name3",
"category": "cat3"
},
{
"name": "name 4",
"category": "cat 4"
}
];
const result = num.reduce((sum, entry) => sum + (entry.category.includes("cat") ? 1 : 0), 0);
console.log(result);
Or of course, a simple for loop.
This is fairly simple. In ES6 the solution would be this:
const countOfCats = num.filter(entry => entry.category.match('cat')).length;
Another way could be:
const countOfCats = num.filter(entry => entry.contains("cat")).length;
I need to filter JSON result using jQuery grep.My JSON result look like this:
var data = { "items":[
{
"Name": "Name 1",
"City": "city1"
},
{
"Name": "Name 2",
"City": "city2"
},
{
"Name": "Name 3",
"City": "cat1"
}
]}
Filter this JSON with array of Name example:
var Name= ["Name 1","Name 2"];
Use jQuery.grep() to filter the items array
var data = {
"items": [{
"Name": "Name 1",
"City": "city1"
}, {
"Name": "Name 2",
"City": "city2"
}, {
"Name": "Name 3",
"City": "cat1"
}]
}
var name = ["Name 1", "Name 2"];
var res = $.grep(data.items, function(v) {
return name.indexOf(v.Name) > -1;
});
document.write('<pre>' + JSON.stringify(res, 0, 3) + '</pre>');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Or with filter()
var data = {
"items": [{
"Name": "Name 1",
"City": "city1"
}, {
"Name": "Name 2",
"City": "city2"
}, {
"Name": "Name 3",
"City": "cat1"
}]
}
var name = ["Name 1", "Name 2"];
var res = data.items.filter(function(v) {
return name.indexOf(v.Name) > -1;
});
document.write('<pre>' + JSON.stringify(res, 0, 3) + '</pre>');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
If you need to get the string array from existing object array using $.grep so first use $.grep for filter the object and then use $.map for get the specific output from the result object like below code will help you.
Filter the object using $.grep
var data = {
"items": [
{
"Name": "Name 1",
"City": "city1"
},
{
"Name": "Name 2",
"City": "city2"
},
{
"Name": "Name 3",
"City": "cat1"
}
]
};
var objret = $.grep(data.items, function (n, i) {
return n.Name == 'Name 1' || n.Name == 'Name 2';
});
Now you have result object in the objret variable now convert the object result to your string array out put using $.map like :-
Get OutPut
var array = $.map(objret, function (value, index) {
return [value.Name];
});
so in the array have your required output.
I think is the same questione exposed here:
[question] Filtering a json array using jquery grep
However json array are like string, or you get an array from that or u can access it only like string.
Use jquery grep to recreate the array then access it via index to compare value
I'm trying to use lodash to compare two arrays of objects and return the difference between the two, then adding it to the original data set. Reason being, the new data will contain the same data from the original. For example, I have 3 objects in orgData, and when I request newData, it will contain the same orgData plus one more object.
var orgData = [{
"id": 1000,
"title": "First item"
}, {
"id": 1001,
"title": "Second item"
}];
var newData = [{
"id": 1000,
"title": "First item"
}, {
"id": 1001,
"title": "Second item"
}, {
"id": 1002,
"title": "Third item"
}];
My only delimiter in comparing is the id which is unique. I've tried the following, but the error I receive is 'Cannot read property of 'id' undefined' which makes sense.
_.filter(orgData, function(o, x) {
return o.id !== newData[x].id;
}).forEach(function(x) {
orgData.push(x);
});
Keep track of ids in a separate data structure:
var orgDataIds = [];
_.each(orgData, function(value) {
orgDataIds.push(value.id);
})
Inspect the id of each object in newData, and add the object to orgData when the corresponding id isn't found in the ids array:
_.each(newData, function(value) {
var id = value.id;
if (orgDataIds.indexOf(id) === -1) {
orgDataIds.push(id);
orgData.push(value);
}
})
It's not clear to me when you want to remove objects from an array though. Are you intending to take the intersection of the two arrays?
var orgData = [{
"id": 1000,
"title": "First item"
}, {
"id": 1001,
"title": "Second item"
}];
var newData = [{
"id": 1000,
"title": "First item"
}, {
"id": 1001,
"title": "Second item"
}, {
"id": 1002,
"title": "Third item"
},
{
"id": 1003,
"title": "Forth item"
}];
var newArray = _.union(orgData,newDate)
console.log(JSON.stringify(newArray))
// this will return union of this two array