Group array items using object will more than 2 elements - javascript

I found this solution for the kind of problem I'm trying to solve here
The only difference is that my array of object has more than 2 elements
and the result wanted is similar to the solution but with all the elements
{
"group": "one",
"color": ["red", "green", "black"],
"size": ["big"],
"date": ["11/08/2018"]
}
So I've been repeating the .map() to get all my values to show but I feel that I shouldn't ...
Can someone please help me with and simpler and better option?
var db = [{"Record":{"id":"26","cost_center":"15073 DC1 M8 - Filmatic","batch_no":"367746","item_code":"12583","description":"LF Fruited Guava (2x6)x15"}},{"Record":{"id":"29","cost_center":"15073 DC1 M8 - Filmatic","batch_no":"367749","item_code":"12583","description":"LF Fruited Guava (2x6)x15"}},{"Record":{"id":"36","cost_center":"15093 DC1 M10 - CornerPot Machi","batch_no":"367756","item_code":"12256","description":"PROMO CP LF SaltedCar w H"}}];
var myArray = [];
for (var i in db) {
if (db.hasOwnProperty(i)) {
myArray.push(db[i].Record);
}
}
var res = myArray.reduce(function(res, elem) {
if (res.indexOf(elem.cost_center) === -1) {
res.push(elem.cost_center);
}
return res;
}, []).map(function(machine) {
return {
cost_center: machine,
batch_no: myArray.filter(function(_el) {
return _el.cost_center === machine;
}).map(function(_el) { return _el.batch_no; }),
item_code: myArray.filter(function(_el) {
return _el.cost_center === machine;
}).map(function(_el) { return _el.item_code; }),
description: myArray.filter(function(_el) {
return _el.cost_center === machine;
}).map(function(_el) { return _el.description; })
}
});
console.log(res);

For the later added code, you could use a hash table, where you collect all objects with the same cost_center and use another array for collecting the values of the properties, like batch_no, item_code and description.
var db = [{ Record: { id: "26", cost_center: "15073 DC1 M8 - Filmatic", batch_no: "367746", item_code: "12583", description: "LF Fruited Guava (2x6)x15" } }, { Record: { id: "29", cost_center: "15073 DC1 M8 - Filmatic", batch_no: "367749", item_code: "12583", description: "LF Fruited Guava (2x6)x15" } }, { Record: { id: "36", cost_center: "15093 DC1 M10 - CornerPot Machi", batch_no: "367756", item_code: "12256", description: "PROMO CP LF SaltedCar w H" } }],
keys = ["batch_no", "item_code", "description"],
hash = Object.create(null),
result = [];
db.forEach(function (o) {
if (!hash[o.Record.cost_center]) {
hash[o.Record.cost_center] = { cost_center: o.Record.cost_center };
keys.forEach(function (k) {
hash[o.Record.cost_center][k] = [o.Record[k]];
});
result.push(hash[o.Record.cost_center]);
return;
}
keys.forEach(function (k) {
hash[o.Record.cost_center][k].push(o.Record[k]);
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

sort an array of objects by 1 or more property values

I am figuring out if there is a better way to sort the below array.
The below code sorts the array based on the label and deviceRegistered.
if label is Desk phone and deviceRegistered is true then it should take the precedence.
My approach is like below.
const terminals = [
{
"device":"JKJCF00",
"directory":"+1899990000",
"label":"Jabber",
"deviceRegistered":"false"
},
{
"device":"IOP8999",
"directory":"9099886644",
"label":"Desk Phone",
"deviceRegistered":"false"
},
{
"device":"KLJ7888",
"directory":"+8999999",
"label":"Jabber",
"deviceRegistered":"true"
},
{
"device":"VFD87987",
"directory":"+12386444",
"label":"Desk Phone",
"deviceRegistered":"true"
}]
let term = [...terminals],arr=[],sortedLines = [],lineObj ={};
term.forEach(line => arr.indexOf(line.label)===-1 ? arr.push(line.label):'');
arr.forEach(device => {
let filterArr = term.filter(line => line.label === device)
let sortArr = [...filterArr].sort((dev1,dev2) => dev1.deviceRegistered !== 'true' ? 1 : dev2.deviceRegistered !== 'true' ? -1 : 0)
lineObj[device] = sortArr
})
for (line in lineObj){ console.log(lineObj[line])
sortedLines.push(...lineObj[line])
}
}
The output is
[
{
"device":"KLJ7888",
"directory":"+8999999",
"label":"Jabber",
"deviceRegistered":"true"
},
{
"device":"JKJCF00",
"directory":"+1899990000",
"label":"Jabber",
"deviceRegistered":"false"
},
{
"device":"VFD87987",
"directory":"+12386444",
"label":"Desk Phone",
"deviceRegistered":"true"
},
{
"device":"IOP8999",
"directory":"9099886644",
"label":"Desk Phone",
"deviceRegistered":"false"
}
]
You could check the properties and use the delta of the boolean values.
const
array = [
{ device: "JKJCF00", directory: "+1899990000", label: "Jabber", deviceRegistered: "false" },
{ device: "IOP8999", directory: "9099886644", label: "Desk Phone", deviceRegistered: "false" },
{ device: "KLJ7888", directory: "+8999999", label: "Jabber", deviceRegistered: "true" },
{ device: "VFD87987", directory: "+12386444", label: "Desk Phone", deviceRegistered: "true" }
];
array.sort((a, b) =>
(b.label === 'Desk Phone') - (a.label === 'Desk Phone') ||
(b.deviceRegistered === 'true') - (a.deviceRegistered === 'true')
);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Assumption: I'm the author of this library, but I created sort-es exactly to simplify the array sorting.
If you're interested : docs GH
import { byValues, byString, byValue, byBoolean } from "sort-es"
const terminals = [] //...items
const sorted = terminals.sort(byValues([
["label", byString()],
["deviceRegistered", byValue(d => d === 'true', byBoolean())]
]))

count all values on object array - javascript

I need to count each value on the object array , the desired output should be like below
[{
"question": "question1",
"USA": 2
}, {
"question": "question1",
"AUS": 1
},
{
"question": "question2",
"item1": 2
},
{
"question": "question2",
"item1,item2": 1
}, {
"question": "question4",
"3": 1
}, {
"question": "question4",
"2": 1
}
]
Below is the input I need to transform in to the above output. I have no clue how to do with n no of question and also got issue when one question has 2 answers . sample input
[{"question1":"USA","question2":["item1"],"question4":2},
{"question1":"USA","question2":["item1"],"question4":3},
{"question1":"AUS","question2":["item1","item2"]}];
let arr=[{"question1":"USA","question2":["item1"],"question4":2},{"question1":"USA","question2":["item1"],"question4":3},{"question1":"AUS","question2":["item1","item2"]}];
//console.log(arr);
function solve(list){
var map = new Map();
var entry = null;
for(var item of list){
if(!map.has(item.question1))
map.set(item.question1, {question:'question1'});
entry = map.get(item.question1);
if(entry.hasOwnProperty(item.question1))
entry[item.question1] = entry[item.question1] + 1;
else
entry[item.question1] = 1;
if(!map.has(item.question2))
map.set(item.question2, {question: 'question2'});
entry = map.get(item.question2);
if(entry.hasOwnProperty(item.question2))
entry[item.question2] = entry[item.question2] + 1;
else
entry[item.question2] = 1;
}
return Array.from(map.values());
}
console.log(solve(arr))
You could take an object or what ever data structure you like which supports a key/value structure in a nested style and collect first all items and then reder the collected tree.
This approach uses objects, because the keys are strings, this is important for an array as key. This is joint with a comma which is sufficient for this use case.
var data = [{ question1: "USA", question2: ["item1"], question4: 2 }, { question1: "USA", question2: ["item1"], question4: 3 }, { question1: "AUS", question2: ["item1", "item2"] }],
hash = data.reduce((hash, o) => {
Object.entries(o).forEach(([question, value]) => {
var sub = hash[question] = hash[question] || Object.create(null);
sub[value] = sub[value] || { question, [value]: 0 };
sub[value][value]++;
});
return hash;
}, Object.create(null)),
result = Object.values(hash).reduce((r, sub) => [...r, ...Object.values(sub)], []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
First, obtain the countries by using reduce. Then use some nested forEach loops for the rest:
const input = [{"question1":"USA","question2":["item1"],"question4":2},
{"question1":"USA","question2":["item1"],"question4":3},
{"question1":"AUS","question2":["item1","item2"]}];
const countriesOutput = input.reduce((acc, curr) => {
if (!acc.some(e => e[curr.question1])) {
acc.push({ question: "question1", [curr.question1]: 1 });
} else {
acc.find(e => e[curr.question1])[curr.question1]++;
}
return acc;
}, []);
let questionsOutput = [];
input.forEach(item => {
Object.keys(item).forEach(key => {
if (key != "question1") {
if (Array.isArray(item[key])) {
questionsOutput.push({ question: key, [item[key].join(",")]: 1 });
} else {
questionsOutput.push({ question: key, [item[key]]: 1 });
}
}
});
});
const finalOutput = [...countriesOutput, ...questionsOutput];
console.log(finalOutput);
.as-console-wrapper { max-height: 100% !important; top: auto; }
Its a matter of summarizing the input using a dictionary (like Object) and track the duplicates. The "name" of the name/value pair can be uniquely identified by combining the question and answer with some delimiter.
const input = [{
"question1": "USA",
"question2": ["item1"],
"question4": 2
},
{
"question1": "USA",
"question2": ["item1"],
"question4": 3
},
{
"question1": "AUS",
"question2": ["item1", "item2"]
}
];
//Sum the input to an array which we can easily search for duplciates
var repeatCounter = {};
input.forEach(objItem => {
Object.keys(objItem).forEach(propItem => {
//Get the counter and the string
var s = `${propItem}-${objItem[propItem]}`;
var c = repeatCounter[s] || 0;
//Modify it or introduce it if absent
repeatCounter[s] = c + 1;
})
})
var output = Object.keys(repeatCounter).map(element => {
var ret = {'question': element.split('-')[0]}
ret[element.split('-')[1]] = repeatCounter[element];
return ret;
})
console.log(output);
.as-console-wrapper {
max-height: 100% !important;
}
Subtle adjustments such as fortifying the delimiter, converting multiple strings in to array items(as shown in the question) needs to be done on practical grounds.

How to change the value of an Array object in Javascript

I have an array object call listOfObjects.
[{"name":"A", "data":"[{"value1":"1","value2":"2"}]"},
{"name":"B", "data":"[{"value1":"1","value2":"2"}]"}]
What I want to do is insert an object into the array where the array is empty.If the array is not empty then do a check on the item inside. If item already exist, do update on the item, else add it to the array. Below is my code
var searchName= "A";
if (listOfObjects.length > 0) {
for (var i = 0; i < listOfObjects.length; i++) {
if (listOfObjects[i].name == searchName) {
listOfObjects[i].data = data;
break;
} else {
insert = {
'name': searchName,
'data': data
};
listOfObjects.push(insert);
}
}
} else {
insert = {
'name': searchName,
'data': data
};
listOfObjects.push(insert);
}
When I run it, even though A already exist, it update the existing item but also add one more time to the listOfObjects. Is there anyway that can achieve what I want? Thanks..
The problem is you're inserting into the array inside your for loop looking for a match. Instead, remember whether you've seen a match and insert after the loop if you haven't. There's also no reason for the length check and no reason to repeat your logic for inserting:
var searchName= "A";
var found = false;
for (var i = 0; !found && i < listOfObjects.length; i++) {
if (listOfObjects[i].name == searchName) {
listOfObjects[i].data = data;
found = true;
}
}
if (!found) {
listOfObjects.push({
'name': searchName,
'data': data
});
}
Note that you can use Array#find (which can be polyfilled for old browsers) to find the entry rather than a for loop if you like:
var searchName= "A";
var entry = listOfObjects.find(function(entry) {
return entry.name == searchName;
});
if (entry) {
entry.data = data;
} else {
listOfObjects.push({
'name': searchName,
'data': data
});
}
First of all change this
[{"name":"A", "data":"[{"value1":"1","value2":"2"}]"},
{"name":"B", "data":"[{"value1":"1","value2":"2"}]"}]
by
[{"name":"A", "data":[{"value1":1,"value2":2}]},
{"name":"B", "data":[{"value1":"1","value2":"2"}]}];
because your list will throw Uncaught SyntaxError: Unexpected identifier
Write another simple function to get the item listOfObjects[i] with selected searchName. Here 'getSearchObject()' function checks the existance of searchName and then add or updates array.
addOrRemoveItem() {
let listOfObjects = [
{ "name": "A", "data": "[{'value1':'1','value2':'2'}]" },
{ "name": "B", "data": "[{'value1':'1','value2':'2'}]" }
],
data = '[{"value1":"1","value2":"2"}]';
var searchName = "C";
if (listOfObjects.length > 0) {
let searchObj = this.getSearchObject(listOfObjects, searchName);
if (searchObj) {
searchObj.data = data;
} else {
let insert = {
"name": searchName,
"data": data
}
listOfObjects.push(insert);
}
} else {
let insert = {
"name": searchName,
"data": data
}
listOfObjects.push(insert);
}
}
getSearchObject(objArr, searchKey) {
var obj = null;
for (let i = 0; i < objArr.length; i++) {
if (objArr[i].name === searchKey) {
obj = objArr[i];
}
}
return obj;
}
A generic solution that recognizes older JS engines (filter instead of find) but does always assume getting passed a list of unique items could be implemented like this ...
function updateList(itemList, item) { // - always assume a list of unique items.
var
itemName = item.name,
listItem = itemList.filter(function (elm) { // - assume `filter`, not find`.
return (elm.name === itemName); // - find/get existing list item by name.
})[0];
if (listItem) {
listItem.data = item.data;
} else {
itemList.push(item)
}
}
var list = [
{ "name": "A", "data": [ { "value1": "A1", "value2": "A2" }] },
{ "name": "B", "data": [ { "value1": "B1", "value2": "B2" }] },
{ "name": "C", "data": [ { "value1": "C1", "value2": "C2" }] }
];
console.log('list : ', list);
updateList(list, { "name": "D", "data": [ { "value1": "D1", "value2": "D2" }] });
updateList(list, { "name": "B", "data": [ { "value1": "b_1", "value2": "b_2", "value3": "b_3" }] });
updateList(list, { "name": "C", "data": [ { "value3": "C3" }] });
console.log('list : ', list);
.as-console-wrapper { max-height: 100%!important; top: 0; }

Print array of objects entire path

Good Day People
I have an array of objects and I need to print out the path of each node value and last print key and value for special (by name) node.
This is the array of objects or JSON
[{
"Name": "2007",
"Elements": [{
"Name": "country1",
"Elements": [{
"House": "house1",
"water": 1.8
}],
"Data": {}
},
{
"Name": "country2",
"Elements": [{
"Name": "city2",
"Elements": [{
"Name": "neighbourhood2",
"Elements": [{
"House": "house2",
"water": 2.8
}]
}],
"Data": {}
}],
"Data": {}
},
{
"Name": "country3",
"Elements": [{
"House": "house2",
"uni bill": 3.8
}],
"Data": {}
}
],
"Data": {}
}]
The output should be like this
2007 > country1 > house > water: 1.8
2007 > city2 > neighbourhood2 > house2 > electricity: 2.8
2007 > country3 > house > uni bill: 3.8
++++++++++++++ edited +++++++++++++++
function objectToPaths(data) {
var validId = /^[a-z_$][a-z0-9_$]*$/i;
var result = [];
doIt(data, "");
return result;
function doIt(data, s) {
if (data && typeof data === "object") {
if (Array.isArray(data)) {
for (var i = 0; i < data.length; i++) {
doIt(data[i], s + "");
}
} else {
for (var p in data) {
if (validId.test(p)) {
doIt(data[p], s + " > " + data[p]);
} else {
doIt(data[p], s + "");
}
}
}
} else {
result.push(s);
}
}
}
this is a rewrite of a function I found here but I did not get the expected result
+++++++++++++++++++++++ end of the edit +++++++++++++++++++++++
Please help
Thanks in advance
What you are looking for is a Depth First Traversal function that recursively print properties:
function print(arr, path) { // print takes an array an an accumulated path from which it will start printing
arr.forEach(function(obj) { // for each object obj in the array
if(obj.Elements) { // if the object obj has sub elements in it
print(obj.Elements, path + " > " + obj.Name); // then call print on those elements, providin the absolute path to this object
} else { // otherwise (it is a leaf)
const bills = Object.keys(obj)
.filter(key => key !== "House")
.map(key => `${key}: ${obj[key]}`)
.join(', ')
console.log(path.slice(3) + " > " + obj.House + " > " + bills); // print the accumulated path along with the House property of this object (removing the first 3 letters from path which are equal to " > ")
}
});
};
var arr = [{"Name":"2007","Elements":[{"Name":"country1","Elements":[{"House":"house1","water":1.8}],"Data":{}},{"Name":"country2","Elements":[{"Name":"city2","Elements":[{"Name":"neighbourhood2","Elements":[{"House":"house2","water":2.8}]}],"Data":{}}],"Data":{}},{"Name":"country3","Elements":[{"House":"house2","uni bill":3.8}],"Data":{}}],"Data":{}}];
print(arr, "");
You could take a function for iterating and collect the path to the last object.
function iter(array, path) {
path = path || [];
array.forEach(function (o) {
if (o.Elements) {
return iter(o.Elements, path.concat(o.Name));
}
Object.keys(o).forEach(function (k) {
if (k !== 'House') {
console.log(path.concat(o.House, k).join(' > ') + ': ' + o[k]);
}
});
});
}
var data = [{ Name: "2007", Elements: [{ Name: "country1", Elements: [{ House: "house1", water: 1.8 }], Data: {} }, { Name: "country2", Elements: [{ Name: "city2", Elements: [{ Name: "neighbourhood2", Elements: [{ House: "house2", water: 2.8 }] }], Data: {} }], Data: {} }, { Name: "country3", Elements: [{ House: "house2", "uni bill": 3.8 }], Data: {} }], Data: {} }];
iter(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to search object with array of inputs AngularJS

I have used the AngularJS filter to search objects and even used the deep search with the $, however partial searches like the below never return the result that I would expect, I can split the search input into an array using .split(" ") though I do not know how to correctly pass this array to the filter.
I have tried to define my own filter with no success.
Data:
[
{
Description: "test Description",
Color: "Blue",
Size: "Large"
}, {
Description: "aest 2",
Color: "orange",
Size: "Large"
}
, {
Description: "test 3",
Color: "black",
Size: "small"
}
]
Search input -> expected behavior
"tes blu" -> return one result
"te desc" -> return one result
"purple" -> return zero results
Any advice on how to proceed would be greatly appreciated.
I am using AngularJS: v1.3.13
code here: http://plnkr.co/edit/j3NqCtO1qHJrMuj6y2nv?p=preview
this is how I solved it: http://plnkr.co/edit/W66NxRlh8deRUh6Uxpor?p=preview
var App = angular.module("App",[]);
App.controller("testCtrl",function($rootScope,$scope){
$scope.testObjects = [
{
Description: "test Description",
Color: "Blue",
Size: "Large"
}, {
Description: "aest 2",
Color: "orange",
Size: "Large"
}
, {
Description: "test 3",
Color: "black",
Size: "small"
}
];
$scope.Search = "";
$rootScope.ScoreThings = function(object , inputArray){
var result = 0;
for (var x = 0; x < inputArray.length; x++)
{
angular.forEach(object, function(value, key) {
if (value.toLowerCase().indexOf(inputArray[x].toLowerCase()) > -1)
{
result ++;
}
});
}
return result;
}
});
App.filter('SearchFilter2', function ($rootScope) {
return function (items, inputString) {
var filtered = [];
var inputArray = inputString.split(" ");
for (var i = 0; i < items.length; i++)
{
if ($rootScope.ScoreThings(items[i], inputArray) > (inputArray.length / 1.2) )
{
filtered.push(items[i]);
}
}
return filtered;
};
});
use it in your view like:
<tr ng-repeat="testObject in testObjects | SearchFilter2 : Search ">

Categories

Resources